split material ui style to other file - reactjs

I wish to remove all the styles from my main component.
All the JS style of the material UI is on the same file and it is starting to get long code.
I wish to create a new file that contains all the styles on other file and on Navbar.js I will just call the components, just same as CSS file that I call to class
import styled from "#emotion/styled";
import { Notifications, Pets } from "#mui/icons-material";
import {
AppBar,
Avatar,
Badge,
Box,
InputBase,
Menu,
MenuItem,
Toolbar,
Typography,
} from "#mui/material";
import MailIcon from "#mui/icons-material/Mail";
import React, { useState } from "react";
const StyledToolbar = styled(Toolbar)({
display: "flex",
justifyContent: "space-between",
});
const Search = styled("div")(({ theme }) => ({
backgroundColor: "white",
padding: "0 10px",
borderRadius: theme.shape.borderRadius,
width: "40%",
}));
const Icons = styled(Box)(({ theme }) => ({
display: "none",
gap: "20px",
alignItems: "center",
[theme.breakpoints.up("sm")]: {
display: "flex",
},
}));
const UserBox = styled(Box)(({ theme }) => ({
display: "flex",
gap: "10px",
alignItems: "center",
[theme.breakpoints.up("sm")]: {
display: "none",
},
}));
const Navbar = () => {
const [open, setOpen] = useState(false);
return (
<AppBar position="sticky">
<StyledToolbar>
<Typography variant="h6" sx={{ display: { xs: "none", sm: "block" } }}>
PALSAM
</Typography>
<Pets sx={{ display: { xs: "block", sm: "none" } }} />
<Search>
<InputBase placeholder="search..." />
</Search>
<Icons>
<Badge badgeContent={4} color="error">
<MailIcon />
</Badge>
<Badge badgeContent={4} color="error">
<Notifications />
</Badge>
<Avatar
sx={{ width: 30, height: 30 }}
src="https://images.pexels.com/photos/846741/pexels-photo-846741.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2"
onClick={(e) => setOpen(true)}
/>
</Icons>
<UserBox onClick={(e) => setOpen(true)}>
<Avatar
sx={{ width: 30, height: 30 }}
src="https://images.pexels.com/photos/846741/pexels-photo-846741.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2"
/>
<Typography variant="span">Jhon</Typography>
</UserBox>
</StyledToolbar>
<Menu
id="demo-positioned-menu"
aria-labelledby="demo-positioned-button"
open={open}
onClose={(e) => setOpen(false)}
anchorOrigin={{
vertical: "top",
horizontal: "right",
}}
transformOrigin={{
vertical: "top",
horizontal: "right",
}}
>
<MenuItem>Profile</MenuItem>
<MenuItem>My account</MenuItem>
<MenuItem>Logout</MenuItem>
</Menu>
</AppBar>
);
};
export default Navbar;

You can create a new js file and add export before each const then you can import them from that file
export const StyledToolbar = styled(Toolbar)({
display: "flex",
justifyContent: "space-between",
});

Related

MUI default dashboard template, localStorage bug

I'm developing nextjs app. For a start I downloaded this default dashboard theme from MUI website https://mui.com/material-ui/getting-started/templates/dashboard/ code: https://github.com/mui/material-ui/blob/v5.10.13/docs/data/material/getting-started/templates/dashboard/Dashboard.tsx
However, when I try to implement localStorage that will store "open" drawer state like in the snippet bellow it bugs- on refresh it's always in the default state, even if the value in localStorage is set to false (when trying to log the stored value, it is correct). When I try to click the button after the refresh with false value nothing happens because it sets the value to true, only next click takes an effect. Any idea what may cause that bug? According to error: Warning: Prop className did not match. Server: "MuiButtonBase-root MuiIconButton-root MuiIconButton-colorInherit MuiIconButton-edgeStart MuiIconButton-sizeMedium css-1johsky-MuiButtonBase-root-MuiIconButton-root" Client: "MuiButtonBase-root MuiIconButton-root MuiIconButton-colorInherit MuiIconButton-edgeStart MuiIconButton-sizeMedium css-dhvns5-MuiButtonBase-root-MuiIconButton-root"
My code:
import * as React from 'react';
import { styled, createTheme, ThemeProvider } from '#mui/material/styles';
import CssBaseline from '#mui/material/CssBaseline';
import MuiDrawer from '#mui/material/Drawer';
import Box from '#mui/material/Box';
import MuiAppBar, { AppBarProps as MuiAppBarProps } from '#mui/material/AppBar';
import Toolbar from '#mui/material/Toolbar';
import List from '#mui/material/List';
import Typography from '#mui/material/Typography';
import Divider from '#mui/material/Divider';
import IconButton from '#mui/material/IconButton';
import Badge from '#mui/material/Badge';
import Container from '#mui/material/Container';
import Grid from '#mui/material/Grid';
import Paper from '#mui/material/Paper';
import Link from '#mui/material/Link';
import MenuIcon from '#mui/icons-material/Menu';
import ChevronLeftIcon from '#mui/icons-material/ChevronLeft';
import NotificationsIcon from '#mui/icons-material/Notifications';
import theme from "../theme";
import { firstListMenu, secondaryListMenu } from './MenuList';
import {useEffect, useState} from "react";
const drawerWidth: number = 200;
interface AppBarProps extends MuiAppBarProps {
open?: boolean;
}
type Props = {
children?: JSX.Element;
}
const AppBar = styled(MuiAppBar, {
shouldForwardProp: (prop) => prop !== 'open',
})<AppBarProps>(({ theme, open }) => ({
zIndex: theme.zIndex.drawer + 1,
transition: theme.transitions.create(['width', 'margin'], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
...(open && {
marginLeft: drawerWidth,
width: `calc(100% - ${drawerWidth}px)`,
transition: theme.transitions.create(['width', 'margin'], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.enteringScreen,
}),
}),
}));
const Drawer = styled(MuiDrawer, { shouldForwardProp: (prop) => prop !== 'open' })(
({ theme, open }) => ({
'& .MuiDrawer-paper': {
position: 'relative',
whiteSpace: 'nowrap',
width: drawerWidth,
transition: theme.transitions.create('width', {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.enteringScreen,
}),
boxSizing: 'border-box',
...(!open && {
overflowX: 'hidden',
transition: theme.transitions.create('width', {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
width: theme.spacing(7),
[theme.breakpoints.up('sm')]: {
width: theme.spacing(9),
},
}),
},
}),
);
const mdTheme = theme;
export default function Layout({children}: Props) {
const [open, setOpen] = useState(() => {
try {
const value = window.localStorage.getItem('open');
if (value) {
return JSON.parse(value);
} else {
window.localStorage.setItem('open', JSON.stringify(true));
return true;
}
} catch (err) {
return true;
}
});
const toggleDrawer = () => {
try {
window.localStorage.setItem('open', JSON.stringify(!open));
} catch (err) {
console.log(err)
}
setOpen(!open);
};
useEffect(() => {
console.log(open)
})
return (
<ThemeProvider theme={mdTheme}>
<Box sx={{ display: 'flex' }}>
<CssBaseline />
<AppBar position="absolute" open={open}>
<Toolbar
sx={{
pr: '24px', // keep right padding when drawer closed
}}
>
<IconButton
edge="start"
color="inherit"
aria-label="open drawer"
onClick={toggleDrawer}
sx={{
marginRight: '36px',
...(open && { display: 'none' }),
}}
>
<MenuIcon />
</IconButton>
<Typography
component="h1"
variant="h6"
color="inherit"
noWrap
sx={{ flexGrow: 1 }}
>
Dashboard
</Typography>
<IconButton color="inherit">
<Badge badgeContent={4} color="secondary">
<NotificationsIcon />
</Badge>
</IconButton>
</Toolbar>
</AppBar>
<Drawer variant="permanent" open={open}>
<Toolbar
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-end',
px: [1],
}}
>
<IconButton onClick={toggleDrawer}>
<ChevronLeftIcon />
</IconButton>
</Toolbar>
<Divider />
<List component="nav">
{firstListMenu}
<Divider sx={{ my: 1 }} />
{secondaryListMenu}
</List>
</Drawer>
<Box
component="main"
sx={{
backgroundColor: (theme) =>
theme.palette.mode === 'light'
? theme.palette.grey[100]
: theme.palette.grey[900],
flexGrow: 1,
height: '100vh',
overflow: 'auto',
}}
>
<Toolbar />
<Container maxWidth="lg" sx={{ mt: 4, mb: 4 }}>
<Grid container spacing={3}>
{/* Chart */}
<Grid item xs={12} md={8} lg={9}>
<Paper
sx={{
p: 2,
display: 'flex',
flexDirection: 'column',
height: 240,
}}
>
{children}
</Paper>
</Grid>
{/* Recent Deposits */}
<Grid item xs={12} md={4} lg={3}>
<Paper
sx={{
p: 2,
display: 'flex',
flexDirection: 'column',
height: 240,
}}
>
</Paper>
</Grid>
{/* Recent Orders */}
<Grid item xs={12}>
<Paper sx={{ p: 2, display: 'flex', flexDirection: 'column' }}>
</Paper>
</Grid>
</Grid>
</Container>
</Box>
</Box>
</ThemeProvider>
);
}
The problem occured because it is rendered on server side. I had to change localStorage to cookies and use getServerSideProps.

MUI React menu never get closed

On this MUI AppBar/Drawer sample, I have added a MUI Menu to edit profile, with an anchor on a div. It's located on the top right corner. I like to be able to click on the username or the profile icon to pop the menu out. For some reason the menu never disappear. I have try to add console.log in the callbacks. It shows the open callback is called right after the close one. Can someone explain why and how to close the menu ?
Here is my code, and the full project on Codesandbox :
import React from "react";
import Box from "#mui/material/Box";
import Grid from "#mui/material/Grid";
import IconButton from "#mui/material/IconButton";
import Menu from "#mui/material/Menu";
import MenuIcon from "#mui/icons-material/Menu";
import MenuItem from "#mui/material/MenuItem";
import Toolbar from "#mui/material/Toolbar";
import AccountCircle from "#mui/icons-material/AccountCircle";
import { styled } from "#mui/material/styles";
import { useNavigate } from "react-router-dom";
export default function TopBar(props) {
const navigate = useNavigate();
const [anchorEl, setAnchorEl] = React.useState(null);
const handleMenuOpen = (event) => {
console.log("set menu open");
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
console.log("set menu close");
setAnchorEl(null);
};
const UserName = styled("div")(({ theme }) => ({
fontSize: "1em",
marginRight: "0.7em",
display: "inline",
height: "100%",
textAlign: "center",
cursor: "pointer"
}));
const Logo = styled("img")(({ theme }) => ({
height: "42px",
width: "42px",
backgroundColor: "#FFF",
marginRight: "0.7em"
}));
return (
<Toolbar>
<Grid
style={{ justifyContent: "space-between", alignItems: "center" }}
container
>
<Grid item style={{ display: "inline-flex", alignItems: "center" }}>
<IconButton
color="inherit"
aria-label="open drawer"
onClick={props.handleDrawerOpen}
edge="start"
sx={{
...(props.drawerOpen && {
display: {
xs: "block",
sm: "none"
}
})
}}
>
<MenuIcon />
</IconButton>
</Grid>
<Grid item style={{ display: "inline-flex", alignItems: "center" }}>
<Logo
src=""
alt="logo"
onClick={() => {
props.handleDrawerClose();
navigate("/");
}}
/>
<Box
sx={{
whiteSpace: "nowrap",
fontSize: { xs: "1em", sm: "1.25em" },
fontWeight: { xs: 400, sm: 500 }
}}
>
Toolbar test
</Box>
</Grid>
<Grid item style={{ display: "flex", alignItems: "center" }}>
<div
style={{ display: "inline-flex", alignItems: "center" }}
onClick={handleMenuOpen}
>
<UserName sx={{ display: { xs: "none", sm: "block" } }}>
{props.userName}
</UserName>
<IconButton
color="inherit"
aria-label="user"
edge="start"
size="large"
>
<AccountCircle />
</IconButton>
<Menu
anchorEl={anchorEl}
open={Boolean(anchorEl)}
onClose={handleClose}
>
<MenuItem
onClick={() => {
props.handleDrawerClose();
navigate("/test");
}}
>
My Profile
</MenuItem>
<MenuItem
onClick={() => {
props.handleDrawerClose();
navigate("/logout");
}}
>
Logout
</MenuItem>
</Menu>
</div>
</Grid>
</Grid>
</Toolbar>
);
}
You somehow managed that when you trigger the onClose event, you are simultaneously triggering handleMenuOpen function. I would suggest you use a button base to handle opening the menu.
Something like this:
<ButtonBase onClick={handleMenuOpen}>
<UserName sx={{ display: { xs: "none", sm: "block" } }}>
{props.userName}
</UserName>
<AccountCircle />
</ButtonBase>
And dont forget to remove the onClick event from your div.
You can also take a look of the fork that i made.

How to create Public and protected Navbar links in Material UI with react and redux?

guys so I'm trying to create a navbar with a set of private navbar links that will be only visible to the user when they log in. Otherwise, the only pages before logging in with a JWT are the register and login page and the register page is set as the '/' directory.
I would like the user prior to registering to see only the /Register, and /Login pages
if the user is logged in I want them to see the /Balance, /Deposit, /Withdraw, and Alldata page
I can't seem to figure this out on MUI can someone help me out, please? Here is my code.
import * as React from 'react';
import AppBar from '#mui/material/AppBar';
import Box from '#mui/material/Box';
import Toolbar from '#mui/material/Toolbar';
import IconButton from '#mui/material/IconButton';
import Typography from '#mui/material/Typography';
import Menu from '#mui/material/Menu';
import MenuIcon from '#mui/icons-material/Menu';
import Container from '#mui/material/Container';
import Avatar from '#mui/material/Avatar';
import Button from '#mui/material/Button';
import Tooltip from '#mui/material/Tooltip';
import MenuItem from '#mui/material/MenuItem';
import AdbIcon from '#mui/icons-material/Adb';
import { Link, useNavigate } from 'react-router-dom'
import { logout, reset } from '../features/auth/authSlice'
import { useSelector, useDispatch } from 'react-redux'
const Navigation = () => {
const navigate = useNavigate()
const dispatch = useDispatch()
const { user } = useSelector((state) => state.auth)
const pages = ['Register', 'Login', 'Deposit', 'Withdraw', 'Alldata'];
const settings = ['Balance', 'Logout'];
const onLogout = () => {
dispatch(logout())
dispatch(reset())
navigate('/')
}
const [anchorElNav, setAnchorElNav] = React.useState(null);
const [anchorElUser, setAnchorElUser] = React.useState(null);
const handleOpenNavMenu = (event) => {
setAnchorElNav(event.currentTarget);
};
const handleOpenUserMenu = (event) => {
setAnchorElUser(event.currentTarget);
};
const handleCloseNavMenu = () => {
setAnchorElNav(null);
};
const handleCloseUserMenu = () => {
setAnchorElUser(null);
};
return (
<AppBar position="static">
<Container maxWidth="xl">
<Toolbar disableGutters>
<AdbIcon sx={{ display: { xs: 'none', md: 'flex' }, mr: 1 }} />
<Typography
variant="h6"
noWrap
component="a"
href="/"
sx={{
mr: 2,
display: { xs: 'none', md: 'flex' },
fontFamily: 'monospace',
fontWeight: 700,
letterSpacing: '.3rem',
color: 'inherit',
textDecoration: 'none',
}}
>
LOGO
</Typography>
<Box sx={{ flexGrow: 1, display: { xs: 'flex', md: 'none' } }}>
<IconButton
size="large"
aria-label="account of current user"
aria-controls="menu-appbar"
aria-haspopup="true"
onClick={handleOpenNavMenu}
color="inherit"
>
<MenuIcon />
</IconButton>
<Menu
id="menu-appbar"
anchorEl={anchorElNav}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'left',
}}
keepMounted
transformOrigin={{
vertical: 'top',
horizontal: 'left',
}}
open={Boolean(anchorElNav)}
onClose={handleCloseNavMenu}
sx={{
display: { xs: 'block', md: 'none' },
}}
>
{pages.map((page) => (
<MenuItem key={page} onClick={handleCloseNavMenu}>
<Typography textAlign="center">
<Link to={`/${page}`}>
{page}
</Link>
</Typography>
</MenuItem>
)
)}
</Menu>
</Box>
<AdbIcon sx={{ display: { xs: 'flex', md: 'none' }, mr: 1 }} />
<Typography
variant="h5"
noWrap
component="a"
href=""
sx={{
mr: 2,
display: { xs: 'flex', md: 'none' },
flexGrow: 1,
fontFamily: 'monospace',
fontWeight: 700,
letterSpacing: '.3rem',
color: 'inherit',
textDecoration: 'none',
}}
>
BADBANK
</Typography>
<Box sx={{ flexGrow: 1, display: { xs: 'none', md: 'flex' } }}>
{pages.map((page) => (
<Button
key={page}
onClick={handleCloseNavMenu}
sx={{ my: 2, color: 'white', display: 'block' }}
>
<Link to={`/${page}`}>
{page}
</Link>
</Button>
))}
</Box>
<Box sx={{ flexGrow: 0 }}>
<Tooltip title="Open settings">
<IconButton onClick={handleOpenUserMenu} sx={{ p: 0 }}>
<Avatar alt="Remy Sharp" src="/static/images/avatar/2.jpg" />
</IconButton>
</Tooltip>
<Menu
sx={{ mt: '45px' }}
id="menu-appbar"
anchorEl={anchorElUser}
anchorOrigin={{
vertical: 'top',
horizontal: 'right',
}}
keepMounted
transformOrigin={{
vertical: 'top',
horizontal: 'right',
}}
open={Boolean(anchorElUser)}
onClose={handleCloseUserMenu}
>
{settings.map((setting) => (
<MenuItem
key={setting}
onClick={onLogout}>
<Typography textAlign="center">{setting}</Typography>
</MenuItem>
))}
</Menu>
</Box>
</Toolbar>
</Container>
</AppBar>
);
};
export default Navigation;
you can make a conditional statement on links array based on auth state
const pages = user ? ['Deposit', 'Withdraw', 'Alldata'] : ['Register', 'Login'];

theme does not recognize material-ui

I am new in material UI.
somehow my code doesn't recognize the theme parameter.
the error is in this line of code on " [theme.breakpoints.up("sm")]"
const Icons = styled(Box)(({ theme }) => ({
display: "none",
gap: "20px",
alignItems: "center",
[theme.breakpoints.up("sm")]: {
display: "flex",
},
}));
It seems that theme is a parameter and nothing more, how can I implement the "theme"?
here is my full page:
import styled from "#emotion/styled";
import { Notifications, Pets } from "#mui/icons-material";
import {
AppBar,
Avatar,
Badge,
Box,
InputBase,
Toolbar,
Typography,
} from "#mui/material";
import MailIcon from "#mui/icons-material/Mail";
import React from "react";
const StyledToolbar = styled(Toolbar)({
display: "flex",
justifyContent: "space-between",
});
const Search = styled("div")(({ theme }) => ({
backgroundColor: "white",
padding: "0 10px",
borderRadius: 10,
width: "40%",
}));
const Icons = styled(Box)(({ theme }) => ({
display: "none",
gap: "20px",
alignItems: "center",
[theme.breakpoints.up("sm")]: {
display: "flex",
},
}));
const UserBox = styled(Box)(({ theme }) => ({
display: "flex",
gap: "10px",
alignItems: "center",
}));
const Navbar = () => {
return (
<AppBar position="sticky">
<StyledToolbar>
<Typography variant="h6" sx={{ display: { xs: "none", sm: "block" } }}>
My App
</Typography>
<Pets sx={{ display: { xs: "block", sm: "none" } }} />
<Search>
<InputBase placeholder="search..." />
</Search>
<Icons>
<Badge badgeContent={4} color="error">
<MailIcon />
</Badge>
<Badge badgeContent={4} color="error">
<Notifications />
</Badge>
<Avatar
sx={{ width: 30, height: 30 }}
src="https://images.pexels.com/photos/846741/pexels-photo-846741.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2"
/>
</Icons>
<UserBox>
<Avatar
sx={{ width: 30, height: 30 }}
src="https://images.pexels.com/photos/846741/pexels-photo-846741.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2"
/>
<Typography variant="span">Jhon</Typography>
</UserBox>
</StyledToolbar>
</AppBar>
);
};
export default Navbar;
The Error:
Uncaught TypeError: Cannot read properties of undefined (reading 'up')
at Navbar.js:31:1
at handleInterpolation (emotion-serialize.browser.esm.js:137:1)
at serializeStyles (emotion-serialize.browser.esm.js:251:1)
at emotion-styled-base.browser.esm.js:131:1
at emotion-element-cbed451f.browser.esm.js:36:1
at renderWithHooks (react-dom.development.js:16141:1)
at updateForwardRef (react-dom.development.js:19968:1)
at beginWork (react-dom.development.js:22391:1)
at HTMLUnknownElement.callCallback (react-dom.development.js:4157:1)
at Object.invokeGuardedCallbackDev (react-dom.development.js:4206:1)
You should import it as named import. Try importing it like this.
import { styled } from '#mui/system';
The problem is that you are destructuring the theme object and passing a theme property who doesn't exist, you should pass the breakpoint property to the destructured parameter, like so:
const Icons = styled(Box)(({ breakpoints }) => ({
display: "none",
gap: "20px",
alignItems: "center",
[breakpoints.up("sm")]: {
display: "flex",
},
}));
See the theme object to know more about the available properties.
The problem is Styled should be in "#mui/material"; not in "#emotion/styled"
enter image description here
enter image description here

Why Button text not get resized in Material UI?

I have a Search button, and some other button, but only Search button text font size not get reduced, do you have any idea why?
import React from "react";
import event1 from "../assets/images/event1.png";
import event2 from "../assets/images/event2.png";
import event3 from "../assets/images/event3.png";
import event4 from "../assets/images/event4.png";
import scene from "../assets/images/scene.png";
import styles from "../assets/styles/HomePage.module.css";
import { Container, Box, TextField, Button } from "#material-ui/core";
import { makeStyles } from "#material-ui/core/styles";
const useStyle = makeStyles({
root: {
backgroundColor: "#f6d7dc",
fontFamily: "ASMBold",
height: "100%"
},
container: {
width: "80%",
paddingTop: "20px"
},
search: {
border: "5px solid #184cdf",
padding: "22px 0",
display: "flex",
justifyContent: "center",
alignItems: "center",
fontSize: "20px",
margin: 0
},
longInput: {
width: "400px"
},
searchButton: {
backgroundColor: "#184cdf",
color: "#ec8562",
textTransform: "uppercase",
fontWeight: "bold",
height: "55px",
width: "100px"
}
});
function HomePage(props) {
const classes = useStyle();
return (
<div className={classes.root}>
<Container maxWidth="xl" className={classes.container} disableGutters>
<Box className={classes.search}>
<TextField variant="outlined" placeholder="city or place" />
<TextField variant="outlined" placeholder="dates" />
<TextField
variant="outlined"
placeholder="artists / venues / events"
className={classes.longInput}
/>
<Button varian="contained" className={classes.searchButton}>
Search
</Button>
</Box>
</Container>
</div>
);
}
export default HomePage;

Resources