React Material UI Grid - reactjs

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

Related

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

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"));

Putting gradient background using makeStyles

For some reason, it doesn't respect background: 'linear-gradient(to right, blue.200, blue.700)' under makeStyles. Why is that? All I need to do is put a gradient background on the entire space. <Container className={classes.root}> should probably be a div, what do you think?
import { useState, useEffect } from 'react';
import type { NextPage } from 'next';
import Container from '#mui/material/Container';
import Box from '#mui/material/Box';
import { DataGrid, GridColDef } from '#mui/x-data-grid';
import { createStyles, Grid, Paper, Theme, Typography } from '#mui/material';
import { makeStyles } from '#mui/styles';
import Skeleton from '#mui/material/Skeleton';
import FormOne from './../src/FormOne';
const LoadingSkeleton = () => (
<Box
sx={{
height: 'max-content',
}}
>
{[...Array(10)].map((_, index) => (
<Skeleton variant="rectangular" sx={{ my: 4, mx: 1 }} key={index} />
))}
</Box>
);
const columns: GridColDef[] = [
{ field: 'id', headerName: 'ID' },
{ field: 'title', headerName: 'Title', width: 300 },
{ field: 'body', headerName: 'Body', width: 600 },
];
const useStyles = makeStyles((theme: Theme) =>
createStyles({
root: {
height: '100vh',
overflow: 'auto',
background: `linear-gradient(to right, blue.200, blue.700)`,
},
})
);
const Home: NextPage = () => {
const classes = useStyles();
const [posts, setPosts] = useState([]);
const [loading, setLoading] = useState(true);
// fetch data from fake API
useEffect(() => {
setInterval(
() =>
fetch('https://jsonplaceholder.typicode.com/posts')
.then((response) => response.json())
.then((data) => {
setPosts(data);
setLoading(false);
}),
3000
);
}, []);
return (
<Container
maxWidth={false}
// sx={{
// height: '100vh',
// overflow: 'auto',
// background: `linear-gradient(to right, ${blue[200]}, ${blue[700]})`,
// }}
className={classes.root}
>
<Container component="main" maxWidth="lg" sx={{ mt: 3, mb: 3 }}>
<Grid container spacing={{ xs: 2, md: 3 }}>
<Grid item xs={6}>
<Paper sx={{ padding: 3 }}>
<Typography component="h1" variant="h4" align="center">
GUI #1
</Typography>
<FormOne />
</Paper>
</Grid>
<Grid item xs={6}>
<Paper sx={{ padding: 3 }}>
<Typography component="h1" variant="h4" align="center">
GUI #2
</Typography>
<FormOne />
</Paper>
</Grid>
<Grid item xs={12}>
<Paper sx={{ padding: 3 }}>
<DataGrid
sx={{ height: '650px' }} // either autoHeight or this
rows={posts}
columns={columns}
pageSize={10}
// autoHeight
rowsPerPageOptions={[10]}
disableSelectionOnClick
disableColumnMenu
disableColumnSelector
components={{
LoadingOverlay: LoadingSkeleton,
}}
loading={loading}
/>
</Paper>
</Grid>
</Grid>
</Container>
</Container>
);
};
export default Home;
I think its because you pass it as a string and it simply doesnt recognice what blue.200 is etc.
try:
background: `linear-gradient(to right, ${blue[200]}, ${blue[700])}`,
#Edit
You actualy need to import color that you want to use from #mui/color
import { blue } from "#mui/material/colors";
and then use it as I mentioned before
here is codesandbox preview and here is codesandbox code
hope this is what we want to achieve
Instead of using background use backgroundImage. This should fix the problem.
The code should be
backgroundImage: `linear-gradient(to right, blue[200], blue[700])`,

How to apply margin between Material-UI Grid items?

How can we add margin (empty space) between Material-UI Grid items?
Container's spacing attribute only adds padding on items.
import React from "react";
import ReactDOM from "react-dom";
import { Grid } from "#material-ui/core";
import { makeStyles } from "#material-ui/core/styles";
const useStyles = makeStyles({
root: {
backgroundColor: "red"
},
root2: {
backgroundColor: "green"
}
});
function App() {
const classes = useStyles();
return (
<Grid container spacing={2}>
<Grid item xs={6} className={classes.root}>
hi
</Grid>
<Grid item xs={6} className={classes.root2}>
hi
</Grid>
</Grid>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Change xs attribute so that the total is less than 12 in that row to have some space.
Add margin: "auto" or any other margin as you wish to your items.
import React from "react";
import ReactDOM from "react-dom";
import { Grid } from "#material-ui/core";
import { makeStyles } from "#material-ui/core/styles";
const useStyles = makeStyles({
root: {
backgroundColor: "red",
margin: "auto"
},
root2: {
backgroundColor: "green",
margin: "auto"
}
});
function App() {
const classes = useStyles();
return (
<Grid container spacing={2}>
<Grid item xs={5} className={classes.root}>
hi
</Grid>
<Grid item xs={5} className={classes.root2}>
hi
</Grid>
</Grid>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

How can I set an static outlined div similar to Material-UI's outlined textfield?

I want wrap some TextFields in a outlined container and I found this answer. This work as i want:
But when I click in an inside textfield all the texfields focused:
This is my code:
import React from "react";
import ReactDOM from "react-dom";
import OutlinedDiv from "./OutlinedDiv";
import TextField from "#material-ui/core/TextField";
import Grid from "#material-ui/core/Grid";
function App() {
return (
<div className="App">
<OutlinedDiv label="DIV">
<Grid container justify="center" alignItems="center" spacing={3}>
<Grid item sm={4} xs={12}>
<TextField label="Text1" variant="outlined" />
</Grid>
<Grid item sm={4} xs={12}>
<TextField label="Text2" variant="outlined" />
</Grid>
<Grid item sm={4} xs={12}>
<TextField label="Text3" variant="outlined" />
</Grid>
<Grid item sm={4} xs={12}>
<TextField label="Text4" variant="outlined" />
</Grid>
<Grid item sm={4} xs={12}>
<TextField label="Text5" variant="outlined" />
</Grid>
<Grid item sm={4} xs={12}>
<TextField label="Text6" variant="outlined" />
</Grid>
</Grid>
</OutlinedDiv>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
How I can achive this style and when click in an inside component only focus the selected component?
A response with another approach and the similar solution for outlined div is welcome.
Thanks in advance.
Below is an approach that does not leverage TextField or FormControl and thus can be safely used to wrap other inputs. This copies some styles from OutlinedInput and the InputLabel styles applied when within a FormControl.
import React from "react";
import ReactDOM from "react-dom";
import InputLabel from "#material-ui/core/InputLabel";
import NotchedOutline from "#material-ui/core/OutlinedInput/NotchedOutline";
import { withStyles } from "#material-ui/core/styles";
import clsx from "clsx";
const styles = {
root: {
position: "relative",
marginTop: "8px"
},
contentWrapper: {
position: "relative"
},
content: {
padding: "18.5px 14px"
},
inputLabel: {
position: "absolute",
left: 0,
top: 0,
// slight alteration to spec spacing to match visual spec result
transform: "translate(0, 24px) scale(1)"
},
notchedOutline: {}
};
const LabelledOutline = ({ classes, id, label, children, className }) => {
const [labelWidth, setLabelWidth] = React.useState(0);
const labelRef = React.useRef(null);
React.useEffect(() => {
const labelNode = ReactDOM.findDOMNode(labelRef.current);
setLabelWidth(labelNode != null ? labelNode.offsetWidth : 0);
}, [label]);
return (
<div className={clsx(className, classes.root)}>
<InputLabel
ref={labelRef}
htmlFor={id}
variant="outlined"
className={classes.inputLabel}
shrink
>
{label}
</InputLabel>
<div className={classes.contentWrapper}>
<div id={id} className={classes.content}>
{children}
<NotchedOutline
className={classes.notchedOutline}
notched
labelWidth={labelWidth}
/>
</div>
</div>
</div>
);
};
export default withStyles(styles)(LabelledOutline);
And below is an example using it both without customization and once with customized colors for the label, outline, and content that changes on hover.
import React from "react";
import ReactDOM from "react-dom";
import { withStyles } from "#material-ui/core/styles";
import LabelledOutline from "./LabelledOutline";
const CustomColorLabelledOutline = withStyles({
root: {
"& $notchedOutline": {
borderColor: "purple"
},
"&:hover $notchedOutline": {
borderColor: "orange"
},
"& $inputLabel": {
color: "green"
},
"&:hover $inputLabel": {
color: "blue"
},
"& $content": {
color: "black"
},
"&:hover $content": {
color: "purple"
}
},
notchedOutline: {},
inputLabel: {},
content: {}
})(LabelledOutline);
function App() {
return (
<div>
<LabelledOutline id="myID" label="My Label">
My Content
</LabelledOutline>
<CustomColorLabelledOutline label="My Label">
My Content with custom label and outline color
</CustomColorLabelledOutline>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
And the obligatory TS version (deeply inspired by #Ryan Cogswell's version):
import React, { ReactElement, ReactNode } from "react";
import InputLabel from "#material-ui/core/InputLabel";
import NotchedOutline from "#material-ui/core/OutlinedInput/NotchedOutline";
import { makeStyles, Theme } from "#material-ui/core/styles";
import clsx from "clsx";
const useStyles = makeStyles((theme: Theme) => ({
root: {
position: "relative",
marginTop: "8px",
},
contentWrapper: {
position: "relative",
},
content: {
// padding: "18.5px 14px",
padding: theme.spacing(1),
},
inputLabel: {
position: "absolute",
left: 0,
top: 0,
// slight alteration to spec spacing to match visual spec result
transform: "translate(0, 24px) scale(1)",
},
notchedOutline: { borderRadius: theme.shape.borderRadius },
}));
interface Props {
id: string;
label: string;
children: ReactNode;
className?: string;
}
export default function Conftainer({ id, label, children, className }: Props): ReactElement {
const [labelWidth, setLabelWidth] = React.useState(0);
const labelRef = React.useRef(null);
React.useEffect(() => {
if (labelRef && labelRef.current && (labelRef.current as any).offsetWidth) {
setLabelWidth((labelRef.current as any).offsetWidth);
}
}, [label]);
const classes = useStyles();
return (
<div className={clsx(className, classes.root)}>
<InputLabel
ref={labelRef}
htmlFor={id}
variant="outlined"
className={classes.inputLabel}
shrink
>
{label}
</InputLabel>
<div className={classes.contentWrapper}>
<div id={id} className={classes.content}>
{children}
<NotchedOutline className={classes.notchedOutline} notched labelWidth={labelWidth} />
</div>
</div>
</div>
);
}
(any edits to get rid of the any more than welcome)
I don't have enough point to post a comment on #AntonOfTheWoods answer (I signed up SO today especially for this question).
Just use const labelRef = React.useRef<HTMLLabelElement>(null); and you should be able to get rid of the any

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