How to pass props to a component being "styled" in MUI? - reactjs

Learning MUI and following the docs pretty well. However, I'm trying to style the Paper component as in the examples, but I want the Paper component to have elevation={3}.
How do I pass that prop to Paper in the below code?
import * as React from 'react';
import { styled } from '#mui/material/styles';
import Box from '#mui/material/Box';
import Paper from '#mui/material/Paper';
import Grid from '#mui/material/Unstable_Grid2';
const Item = styled(Paper)(({ theme }) => ({
backgroundColor: theme.palette.mode === 'dark' ? '#1A2027' : '#fff',
...theme.typography.body2,
padding: theme.spacing(1),
textAlign: 'center',
color: theme.palette.text.secondary,
}));
export default function BasicGrid() {
return (
<Box sx={{ flexGrow: 1 }}>
<Grid container spacing={2}>
<Grid xs={8}>
<Item>xs=8</Item>
</Grid>
<Grid xs={4}>
<Item>xs=4</Item>
</Grid>
<Grid xs={4}>
<Item>xs=4</Item>
</Grid>
<Grid xs={8}>
<Item>xs=8</Item>
</Grid>
</Grid>
</Box>
);
}

You can pass the prop and access it. within the Styled component. Remember these are mostly passed as HTML attributes so boolean works a little differently as a string.
Here's a working example at Codesandbox. You can check how we have added a border to the item based on the prop.
import * as React from "react";
import ReactDOM from "react-dom";
import { styled } from "#mui/material/styles";
import Box from "#mui/material/Box";
import Paper from "#mui/material/Paper";
import Grid from "#mui/material/Unstable_Grid2";
function App() {
return (
<Box sx={{ flexGrow: 1 }}>
<Grid container spacing={2}>
<Grid xs={8}>
<Item border={"true"}>xs=8</Item>
</Grid>
<Grid xs={4}>
<Item>xs=4</Item>
</Grid>
<Grid xs={4}>
<Item>xs=4</Item>
</Grid>
<Grid xs={8}>
<Item>xs=8</Item>
</Grid>
</Grid>
</Box>
);
}
const Item = styled(Paper)(({ theme, border }) => ({
backgroundColor: theme.palette.mode === "dark" ? "#1A2027" : "#fff",
...theme.typography.body2,
padding: theme.spacing(1),
textAlign: "center",
border: border === "true" ? "1px solid red" : "none",
color: theme.palette.text.secondary
}));
ReactDOM.render(<App />, document.querySelector("#app"));

Related

how place a button at the center of the image on hover with Material UI (MUI) and reacts?

I have tried looking online and there are some examples of hovering using material ui and more specifically there are some examples using the CardMedia from MUI but I am having trouble adapting it to my case.
I want to add transparency to the existing picture and add a button in the center when the user hovers over the image.
But so far this is the code I have:
const [Hover, setHover] = useState(false);
const handleMouseEnter = () => {
setHover(true);
}
const handleMouseLeave = () => {
setHover(false);
}
const Butt = <Button variant="contained">Get a Free Quote</Button>
return (
<Box p={5}>
<Grid container spacing={5} justify="center">
{images.map((product, i) => {
return (
<Grid key={i} item xs={12} sm={6} md={4}>
<Card sx={{ minWidth: 200 }}>
<CardMedia
component="img"
height="200"
image={product.img}
alt="work portfolio"
onMouseOver={handleMouseEnter}
onMouseOut={handleMouseLeave}/>
</Card>
{Hover && (
<div>
<Butt/>
</div>
)}
</Grid>
);
})}
</Grid>
</Box>
)
using useState you can toggle between display=none to not display the button on onMouseLeave and display=block to display the button onMouseEnter
this is an example using your code :
import React, { useState } from "react";
import "./style.css";
import Grid from "#material-ui/core/Grid";
import Button from "#material-ui/core/Button";
import Box from "#material-ui/core/Box";
import Card from "#material-ui/core/Card";
import CardMedia from "#material-ui/core/CardMedia";
const Butt = ({ display }) => {
return (
<div className={display}>
<Button
style={{
position: "absolute",
top: "80%",
left: "50%",
transform: "translate(-50%, -50%)",
}}
variant="contained"
>
Get a Free Quote
</Button>
</div>
);
};
export default function App() {
const [display, setDisplay] = useState("notdisplayed");
const showButton = (e) => {
e.preventDefault();
setDisplay("displayed");
};
const hideButton = (e) => {
e.preventDefault();
setDisplay("notdisplayed");
};
return (
<Box p={5}>
<Grid container spacing={5} justifyContent="center">
<Grid item xs={12} md={4} sm={6}>
<Card
sx={{ minWidth: 200 }}
style={{ position: "relative", width: "100%" }}
>
<div
onMouseEnter={(e) => showButton(e)}
onMouseLeave={(e) => hideButton(e)}
>
<CardMedia
style={{
marginLeft: "auto",
marginRight: "auto",
width: "100%",
height: "auto",
zIndex: "1",
}}
component="img"
height="200"
image="https://st.depositphotos.com/1001894/3115/i/600/depositphotos_31157709-stock-photo-hassan-ii-mosque-in-casablanca.jpg"
alt="work portfolio"
/>
<Butt display={display} />
</div>
</Card>
</Grid>
</Grid>
</Box>
);
}
add style.css to make these styling in it :
.notdisplayed {
display: none;
}
.displayed {
display: block;
}
this is a demo in codesandbox .
You can achieve the same with just css. Have a look at the code below and this working codesandbox
What it does is just creating a Container that has the image and the Button. Then I styled the button to be hidden initially and is shown when hovering on the container (which is your Card).
import * as React from "react";
import { styled } from "#mui/material/styles";
import Card from "#mui/material/Card";
import CardMedia from "#mui/material/CardMedia";
import Grid from "#mui/material/Grid";
import Button from "#mui/material/Button";
const ButtonStyled = styled(Button)`
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: auto;
width: fit-content;
height: 40px;
display: none;
`;
const ContainerStyled = styled("div")`
position: absolute;
&:hover {
.test-button {
display: block;
}
}
}`;
export default function RecipeReviewCard() {
return (
<Grid container spacing={5} justify="center">
<Grid item xs={12} sm={6} md={4}>
<ContainerStyled>
<Card sx={{ minWidth: 200 }}>
<CardMedia
component="img"
height="200"
image="https://mui.com/static/images/cards/paella.jpg"
alt="work portfolio"
/>
</Card>
<ButtonStyled variant="contained" className="test-button">
Test button
</ButtonStyled>
</ContainerStyled>
</Grid>
<Grid item xs={12} sm={6} md={4}>
<ContainerStyled>
<Card sx={{ minWidth: 200 }}>
<CardMedia
component="img"
height="200"
image="https://mui.com/static/images/cards/paella.jpg"
alt="work portfolio"
/>
</Card>
<ButtonStyled variant="contained" className="test-button">
Test button 2
</ButtonStyled>
</ContainerStyled>
</Grid>
</Grid>
);
}
Let me know if it helps.

useMediaQuery lg Theme Breakpoint Not Breaking at 1280px

I'm trying to create an Ad component on the right hand side of the Page component that disappears whenever the screen size gets below 1280px (MUI lg). Without adding height or content, this works fine, but when the components have a height of 600px or so, the breakpoint shortens to about 1200px. This causes the Ad component to snap below the Page for about 80px or so.
Here's a codesandbox of my desired outcome: code
Here's a codesandbox of what's currently happening when I add height & content: code
(adjust the screen width to see what I'm talking about)
Here's my code:
import useMediaQuery from "#mui/material/useMediaQuery";
import { useTheme } from "#mui/material/styles";
import Box from "#material-ui/core/Box";
import { Grid } from "#material-ui/core";
export default function App() {
const theme = useTheme();
const lgScreen = useMediaQuery(theme.breakpoints.up("lg"));
return (
<Box p={5}>
<Grid container spacing={3}>
<Grid item xs={12} sm={12} md={12} lg>
<Box sx={{ bgcolor: "warning.main", height: "600px" }}>Page</Box>
</Grid>
<Grid item style={{ width: 335 }}>
{lgScreen ? (
<Box sx={{ bgcolor: "secondary.main", height: "600px" }}>Ad</Box>
) : null}
</Grid>
</Grid>
</Box>
);
}

How can I reuse code wrapping Material-UI's Grid while varying the number of columns?

I would like the second grid block under the title "Other Projects of Note" to be mapped through as a 3 column grid. How can I do this without creating a new component? Material-UI controls it's columns with the grid item xs={12} sm={6} and on the 3 column grid I'd need it to read as grid item xs={12} sm={6} lg={4}.
It seems like I'd be copying and pasting the <Card /> component and renaming it to achieve this. I'd like to avoid that. Demo below:
codesandbox
Here's the code for my current Card component:
import React from 'react';
import Grid from '#material-ui/core/Grid';
import Card from '#material-ui/core/Card';
import CardActionArea from '#material-ui/core/CardActionArea';
import CardActions from '#material-ui/core/CardActions';
import { makeStyles } from '#material-ui/core/styles';
import { loadCSS } from 'fg-loadcss';
import CardContent from '#material-ui/core/CardContent';
import CardMedia from '#material-ui/core/CardMedia';
import Button from '#material-ui/core/Button';
import Typography from '#material-ui/core/Typography';
import Box from '#material-ui/core/Box';
import Icon from '#material-ui/core/Icon';
import GitHubIcon from '#material-ui/icons/GitHub';
import Tooltip from '#material-ui/core/Tooltip';
import Zoom from '#material-ui/core/Zoom';
import Link from '#material-ui/core/Link';
const useStyles = makeStyles((theme) => ({
root: {
maxWidth: '100%',
flexGrow: 1,
},
cardGrid: {
paddingTop: theme.spacing(2),
},
media: {
height: 340,
},
button: {
margin: theme.spacing(1),
},
arrow: {
color: theme.palette.common.black,
},
tooltip: {
backgroundColor: theme.palette.common.black,
},
icons: {
'& > .fab': {
marginRight: theme.spacing(4),
},
margin: '1rem 0',
},
}));
function TwoCard(props) {
const classes = useStyles();
React.useEffect(() => {
const node = loadCSS(
'https://use.fontawesome.com/releases/v5.12.0/css/all.css',
document.querySelector('#font-awesome-css')
);
return () => {
node.parentNode.removeChild(node);
};
}, []);
return (
<>
<Grid item xs={12} sm={6}>
<Card>
<CardActionArea>
<CardMedia
className={classes.media}
image={props.card.image}
title='Contemplative Reptile'
/>
<CardContent className={classes.content}>
<Typography gutterBottom variant='h5' component='h2'>
{props.card.project}
</Typography>
<Typography variant='subtitle1' gutterBottom>
{props.card.framework}
</Typography>
<Typography gutterBottom variant='body2' component='p'>
{props.card.description}
</Typography>
<Box className={classes.icons}>
<Typography gutterBottom variant='subtitle2'>
TOOLS USED
</Typography>
<Tooltip
TransitionComponent={Zoom}
arrow
title='REACT'
aria-label='react'
>
<Icon className='fab fa-react fa-3x' color='primary' />
</Tooltip>
<Tooltip
TransitionComponent={Zoom}
arrow
title='HTML5'
aria-label='add'
>
<Icon className='fab fa-html5 fa-3x' color='primary' />
</Tooltip>
<Tooltip
TransitionComponent={Zoom}
arrow
title='CSS3'
aria-label='add'
>
<Icon className='fab fa-css3 fa-3x' color='primary' />
</Tooltip>
</Box>
</CardContent>
</CardActionArea>
<CardActions>
<Button variant='outlined' size='small' color='primary'>
<Link
href={props.card.projectUrl}
target='_blank'
rel='noopener noreferrer'
className={classes.links}
underline='none'
color='inherit'
>
View Project
</Link>
</Button>
<Button
variant='outlined'
size='small'
color='primary'
className={classes.button}
endIcon={<GitHubIcon />}
>
<Link
href={props.card.githubUrl}
target='_blank'
rel='noopener noreferrer'
underline='none'
color='inherit'
>
Code
</Link>
</Button>
</CardActions>
</Card>
</Grid>
</>
);
}
export default TwoCard;
and here's the code that uses that Card component:
import React from "react";
import Grid from "#material-ui/core/Grid";
import Card from "./Card.js";
import { Typography } from "#material-ui/core";
import { makeStyles } from "#material-ui/core/styles";
const useStyles = makeStyles((theme) => ({
headings: {
padding: "20px 0"
}
}));
function Projects(props) {
const classes = useStyles();
let cards = props.data.map((card, i) => {
return <Card card={card} key={i} />;
});
return (
<>
<Typography variant="h4" gutterBottom>
Featured Projects
</Typography>
<Grid container spacing={2}>
{cards}
</Grid>
<Typography variant="h4" className={classes.headings}>
Other Projects of note
</Typography>
<Grid container spacing={2}>
{cards}
</Grid>
</>
);
}
export default Projects;
You can pass a prop to your Card component to control whether it is two-column or three-column. For instance, you can pass a maxColumns prop and use it in the following manner:
<Grid item xs={12} sm={6} lg={maxColumns > 2 ? 4 : undefined}>
Then your Projects component can pass the prop (in my example, I'm defaulting maxColumns to 2, so it doesn't need to be specified in that case):
import React from "react";
import Grid from "#material-ui/core/Grid";
import Card from "./Card.js";
import { Typography } from "#material-ui/core";
import { makeStyles } from "#material-ui/core/styles";
const useStyles = makeStyles((theme) => ({
headings: {
padding: "20px 0"
}
}));
function Projects(props) {
const classes = useStyles();
let twoColumnCards = props.data.map((card, i) => {
return <Card card={card} key={i} />;
});
let threeColumnCards = props.data.map((card, i) => {
return <Card maxColumns={3} card={card} key={i} />;
});
return (
<>
<Typography variant="h4" gutterBottom>
Featured Projects
</Typography>
<Grid container spacing={2}>
{twoColumnCards}
</Grid>
<Typography variant="h4" className={classes.headings}>
Other Projects of note
</Typography>
<Grid container spacing={2}>
{threeColumnCards}
</Grid>
</>
);
}
export default Projects;

React Material UI Grid

Is there any correct and easy way to create kind of table with Material UI ?. direction="column" or direction="row" are not working if I want to have different height columns. Any suggestions?
If you want to use Material-ui built-in Grid system you can do it by using 2 grid containers, one with direction="row" (default) and second (child) with direction="column".
It needs a bit of individual styling, might look "hacky", but I don't know other way:
import React from "react";
import ReactDOM from "react-dom";
import Grid from "#material-ui/core/Grid";
import Box from "#material-ui/core/Box";
import { makeStyles } from "#material-ui/styles";
const useStyles = makeStyles({
box: {
height: "100%",
width: "100%"
},
container: {
height: "400px"
},
innerContainer: {
height: "100%"
},
item: {
flex: 1
}
});
function App() {
const classes = useStyles();
return (
<Grid spacing={4} className={classes.container} container>
<Grid xs={4} item>
<Grid
spacing={4}
direction="column"
className={classes.container}
container
>
<Grid className={classes.item} item>
<Box className={classes.box} bgcolor="blue" />
</Grid>
<Grid className={classes.item} item>
<Box className={classes.box} bgcolor="red" />
</Grid>
</Grid>
</Grid>
<Grid xs={8} item>
<Box className={classes.box} bgcolor="green" />
</Grid>
</Grid>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
LIVE EXAMPlE
Other option is to use CSS Grid. It requires much less elements, and (at least for me) is simplier. However, it doesn't work in Internet Explorer, if you care about such thing.
import React from "react";
import ReactDOM from "react-dom";
import Box from "#material-ui/core/Box";
import { makeStyles } from "#material-ui/styles";
const useStyles = makeStyles({
container: {
height: '400px',
width: "100%",
display: 'grid',
gridTemplateColumns: '1fr 2fr',
gridTemplateRows: '1fr 1fr',
gridGap: "20px",
},
firstChild: {
gridRow: '1 / 2',
gridColumn: '1 / 2',
},
secondChild: {
gridRow: '1 / 3',
gridColumn: '2 / 3',
},
thirdChild: {
gridRow: '2 / 3',
gridColumn: '1 / 2',
},
});
function App() {
const classes = useStyles();
return (
<Box className={classes.container}>
<Box className={classes.firstChild} bgcolor="blue" />
<Box className={classes.secondChild} bgcolor="red" />
<Box className={classes.secondThird} bgcolor="green" />
</Box>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
LIVE EXAMPLE

How do I set container within React Material UI's AppBar Component?

When I see MUI's default AppBar, its children looks so apart especially in wide screen size. The logo is located completely left, and other navs is located too much right. So Items look so apart each other.
What I want to do is like Bootstrap's component, I want to apply maximum width like below image. How do I set container within AppBar?
This is what I tried.
<AppBar>
<ToolBar>
<Grid
container
style = {{ maxWidth: 1170 }}
>
<Typography>Logo</Typography>
</Grid>
</ToolBar>
</AppBar>
But it's not worked...
You may try using
<CssBaseline />
<AppBar position="static">
<Container maxWidth="lg">
<ToolBar>
<Typography>Logo</Typography>
</ToolBar>
</Container>
</AppBar>
This is how I do it:
import AppBar from "#material-ui/core/AppBar";
import { makeStyles } from "#material-ui/core/styles";
import Toolbar from "#material-ui/core/Toolbar";
import React from "react";
const useStyles = makeStyles(() => ({
toolBar: {
margin: "auto",
maxWidth: 800,
width: "100%"
},
}));
export default function() {
const classes = useStyles();
return (
<AppBar>
<Toolbar className={classes.toolBar}>
{...}
</Toolbar>
</AppBar>
);
}
You can try this:
function MyAppbar() {
const classes = makeStyles(theme => createStyles({
root: {
flexGrow: 1,
},
appBar: {
display: 'flex',
justifyContent: 'center',
flexDirection: 'row'
},
toolBar: {
width: '100%',
maxWidth: 1170
}
}))()
return (
<div className={classes.root}>
<AppBar position="static" className={classes.appBar}>
<Toolbar variant="dense" className={classes.toolBar}>
...
</Toolbar>
</AppBar>
</div>
)
}
You can try to use withStyles that is built in material-ui
import React from 'react';
import { withStyles } from '#material-ui/core/styles';
import { AppBar, ToolBar, Grid, Typography } from '#material-ui/core';
const styles = {
toolbar: {
maxWidth: 1170
}
}
class App extends React.Component {
render() {
return (
<AppBar>
<ToolBar
className={this.props.classes.toolbar}
>
<Grid
container
>
<Typography>Logo</Typography>
</Grid>
</ToolBar>
</AppBar>
)
}
}
export default withStyles(styles)(App);

Resources