Appbar and Drawer transitions not working - reactjs

To make this layout I am using the MUI Dashboard example.
https://github.com/mui/material-ui/blob/v5.11.7/docs/data/material/getting-started/templates/dashboard/Dashboard.js
I'm not using styled as engine, but styled-components.
const MainLayout = () => {
const themeAux = useTheme();
const [open, setOpen] = useState(false);
const toggleDrawer = () => setOpen(!open);
const DrawerContent = () => (
<Fragment>
<Toolbar
sx={{
display: 'flex',
justifyContent: 'flex-end',
alignItems: 'center',
px: [1],
}}
>
<IconButton onClick={toggleDrawer}>
<ChevronLeft />
</IconButton>
</Toolbar>
<Divider />
<List component={'nav'}>
<ListItemButton component={'a'} href={''}>
<ListItemIcon>
<Drafts />
</ListItemIcon>
<ListItemText primary={'Main'} />
</ListItemButton>
</List>
</Fragment>
);
const TopBar = () => (
<AppBar
position={'absolute'} // fixed
sx={{
zIndex: (theme) => theme.zIndex.drawer + 1,
transition: (theme) =>
theme.transitions.create(['width', 'margin'], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
...(open && {
ml: { sm: drawerWidth },
width: { sm: `calc(100% - ${drawerWidth}px)` },
transition: (theme) =>
theme.transitions.create(['width', 'margin'], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.enteringScreen,
}),
}),
}}
>
<Toolbar>
<IconButton
sx={{ mr: 2, ...(open && { display: { sm: 'none' } }) }}
edge={'start'}
onClick={toggleDrawer}
>
<Menu />
</IconButton>
<Typography component={'h4'} variant={'h4'} noWrap sx={{ flexGrow: 1 }}>
HEADERS
</Typography>
</Toolbar>
</AppBar>
);
const LargeDrawer = () => (
<Drawer
variant={'permanent'}
open={open}
sx={{
'& .MuiDrawer-paper': {
position: 'relative',
whiteSpace: 'nowrap',
width: drawerWidth,
transition: (theme) =>
theme.transitions.create('width', {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.enteringScreen,
}),
boxSizing: 'border-box',
...(!open && {
overflowX: 'hidden',
transition: (theme) =>
theme.transitions.create('width', {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
width: (theme) => ({ xs: theme.spacing(7), sm: theme.spacing(9) }),
}),
},
}}
>
<DrawerContent />
</Drawer>
);
return (
<Box
sx={{
display: 'flex',
}}
>
<TopBar />
<LargeDrawer />
<Box
component={'main'}
sx={{
flexGrow: 1,
height: '100vh',
overflow: 'auto',
}}
>
<Toolbar />
<Container maxWidth={'lg'} sx={{ my: 4 }}>
<Outlet />
</Container>
</Box>
</Box>
);
};
export default MainLayout;
What am I doing wrong, that causes the transitions not to work when opening and closing the drawer?

Related

How to set data in modal in nextjs

I'm having trouble getting the data and displaying it on the modal interface when editing, usually I will use useState to update the data but is there any way I can pass the data through the variable?.thanks for your help !
view
Here is my code:
this is my index,
I'm trying to get the name and tags back and show the edit form
this is my index
import type { NextPage } from 'next'
import { useEffect, useState } from 'react'
import {
Box,
Button,
Grid,
Icon,
IconButton,
Menu,
MenuItem,
Typography,
} from '#mui/material'
import { MainLayout } from 'components/Layout/MainLayout'
import { HideAppBar } from '../AppBar/HideAppBar'
import { TagProps } from './components/Tag'
import { TraitProps } from './components/Trait'
import { TraitsPickerModal } from './components/TraitsPickerModal'
import { CreateTagCategoryModal } from './components/CreateTagCategoryModal'
import { ALL_TAG, TagCategoryList } from './components/AllTag'
import { Trait } from './components/Trait'
import EditOutlinedIcon from '#mui/icons-material/EditOutlined'
import DeleteOutlinedIcon from '#mui/icons-material/DeleteOutlined'
import { useStaticTagsViewModelSWR } from './hooks/useStaticArtViewModelSWR'
import { TagCategoryEditModal } from './components/Modal/TagCategoryEditModal'
import { TagCategoryDeleteModal } from './components/Modal/TagCategoryDeleteModal'
import { BlackButton } from 'components/Elements/Button/BlackButton'
import { MaterialIconFont } from 'assets/Styles/GlobalStyles'
import {
useStaticSWRTagsListViewModel,
StaticSWRTagstListKey,
} from './hooks/useStaticSWRTagsListViewModel'
import { mutate } from 'swr'
import { TagCategoryListData } from './interface'
const CONTENT_PADDING = '16px'
const dummyTagCategoryList = [
{
name: 'testes',
tagList: [
{
name: 'niceTag',
traitList: [
{
category: 'Clothes',
name: 'Polka',
icon: '/images/test/body4.png',
},
{
category: 'Clothes',
name: 'Dress',
icon: '/images/test/body5.png',
},
],
},
{ name: 'badTag', traitList: [] },
],
},
{
name: 'cat2',
tagList: [
{ name: 'tag1', traitList: [] },
{ name: 'tag2', traitList: [] },
],
},
]
type TaggedListType = TraitProps[][][]
type UntaggedListType = Record<string, TraitProps[]>[]
type TagCategoryListKey = keyof TagCategoryList
const Tags: NextPage = () => {
/**
* state
*/
const [tagCategoryList, setTagCategoryList] = useState<TagProps[]>([])
const [createModalOpen, setCreateModalOpen] = useState(false)
const [traitsPickerOpen, setTraitsPickerOpen] = useState(false)
const [categoryIndex, setCategoryIndex] = useState(0)
const [tagIndex, setTagIndex] = useState(0)
const [taggedList, setTaggedList] = useState<TaggedListType>([])
const [unTaggedList, setUntaggedList] = useState<UntaggedListType>([{}])
const [isVisibleUntaggedList, setIsVisibleUntaggedList] = useState<boolean[]>(
[...new Array(tagCategoryList.length)].map((n) => Boolean(n))
)
const [categoryAnchorElList, setCategoryAnchorElList] = useState(
[...new Array(tagCategoryList.length)].map(() => null)
)
const [categoryAnchorEl, setCategoryAnchorEl] = useState<null | HTMLElement>(
null
)
const [tagCategoryListData, setTagCategoryListData] = useState<
TagCategoryListData[]
>([])
const categoryMenuOpen = Boolean(categoryAnchorEl)
const [idItemTag, setIdItemTag] = useState(0)
/**
* Hook
*/
const { viewModel, setViewModel } = useStaticTagsViewModelSWR()
const { viewModelTagCategory, setViewModelTagCategory } =
useStaticSWRTagsListViewModel()
/**
* useEffect
*/
useEffect(() => {
setTagCategoryList(dummyTagCategoryList)
}, [])
useEffect(() => {
mutate(StaticSWRTagstListKey)
}, [])
useEffect(() => {
setTagCategoryListData(viewModelTagCategory)
}, [viewModelTagCategory])
useEffect(() => {
setTaggedList((list) =>
Object.assign(
tagCategoryList.map((category) => {
return category.tagList.reduce((memo, tag) => {
return tag.traitList
? [...memo, tag.traitList.map((trait) => trait)]
: memo
}, [])
}),
list
)
)
setIsVisibleUntaggedList((list) => [...list, false])
}, [tagCategoryList])
useEffect(() => {
const categoryUsedTraitList = taggedList.reduce((memo, list) => {
return [...memo, list.flat(2)]
}, [])
setUntaggedList(
categoryUsedTraitList.map((list) => {
return Object.keys(ALL_TAG).reduce((memo, key: TagCategoryListKey) => {
memo[key] = ALL_TAG[key].filter((trait) => {
return list.every((item) => {
return item.name !== trait.name
})
})
return memo
}, {} as TagCategoryList)
})
)
}, [taggedList])
/**
* Handler
*/
const onClickTrait = (pickedList: TraitProps[]) => {
setTaggedList((list) => {
list[categoryIndex][tagIndex] = pickedList
return JSON.parse(JSON.stringify(list))
})
}
const onClickToggleUntaggedComponentsBtn = (index: number) => {
setIsVisibleUntaggedList((list) => {
list[index] = !list[index]
return [...list]
})
}
const addTagCategory = (tagCategory: TagCategoryListData) => {
setViewModelTagCategory([...viewModelTagCategory, tagCategory])
}
const menuClick = (
event: React.MouseEvent<HTMLElement>,
idItemTag: number
) => {
setIdItemTag(idItemTag)
setCategoryAnchorEl(event.currentTarget)
}
const menuClose = () => {
setCategoryAnchorEl(null)
}
const openTagCategoryEditModal = (idItemTag: number) => {
const item = viewModelTagCategory.find((tagId) => tagId.id === idItemTag)
console.log(item?.name)
const itemTags = item?.tagList.map((tag) => {
return {
id: tag.id,
name: tag.name,
}
})
console.log(itemTags)
setViewModel({ ...viewModel, tagCategoryEditModal: { isOpen: true } })
setCategoryAnchorEl(null)
}
const openTagCategoryDeleteModal = (idItemTag: number) => {
console.log(idItemTag)
setViewModel({ ...viewModel, tagCategoryDeleteModal: { isOpen: true } })
setCategoryAnchorEl(null)
}
return (
<MainLayout>
<Box sx={{ display: 'flex', flexDirection: 'column' }}>
<HideAppBar tabNum={2} />
<MaterialIconFont />
<TagCategoryEditModal />
<TagCategoryDeleteModal />
<Grid
container
sx={{
position: 'relative',
display: 'block',
textAlign: 'left',
padding: CONTENT_PADDING,
}}
>
<Typography
variant="h5"
style={{
textAlign: 'left',
fontWeight: 'bold',
}}
>
Tags
</Typography>
<Box sx={{ display: 'flex' }}></Box>
<Typography
variant="h6"
style={{ color: '#888888', textAlign: 'left' }}
>
Use tags to organize and group your token traits.
</Typography>
<BlackButton
onClick={() => setCreateModalOpen(true)}
sx={{
position: 'absolute',
right: CONTENT_PADDING,
top: CONTENT_PADDING,
paddingRight: '16px',
fontWeight: 'bold',
fontSize: '0.9rem',
}}
>
<Icon sx={{ marginRight: '8px' }}>add</Icon>New Tag Category
</BlackButton>
{viewModelTagCategory && (
<Box>
{viewModelTagCategory.map((tagCategory, i) => (
<Box key={tagCategory.name + i}>
<Box className="category-box" sx={{ marginTop: '16px' }}>
<Box sx={{ display: 'flex', alignItems: 'center' }}>
<Typography
variant="subtitle1"
sx={{ fontWeight: 'bold' }}
>
{tagCategory.name}_{tagCategory.id}
</Typography>
<IconButton
aria-label="more"
id={`tag-category-menu-btn-${tagCategory.id}`}
aria-controls={
categoryMenuOpen
? `tag-category-menu-${tagCategory.id}`
: undefined
}
aria-expanded={categoryMenuOpen ? 'true' : undefined}
aria-haspopup="true"
data-category-num={tagCategory.id}
onClick={(e) => menuClick(e, tagCategory.id)}
>
<Icon>more_vert</Icon>
</IconButton>
<Menu
id={`tag-category-menu-${tagCategory.id}`}
MenuListProps={{
'aria-labelledby': `tag-category-menu-btn-${tagCategory.id}`,
}}
anchorEl={categoryAnchorEl}
open={categoryMenuOpen}
onClose={menuClose}
>
<MenuItem
onClick={() => openTagCategoryEditModal(idItemTag)}
>
<EditOutlinedIcon sx={{ mr: 1 }} />
<Typography>Edit</Typography>
</MenuItem>
<MenuItem
onClick={() => openTagCategoryDeleteModal(idItemTag)}
sx={{ color: 'red' }}
>
<DeleteOutlinedIcon sx={{ mr: 1 }} />
<Typography>Delete</Typography>
</MenuItem>
</Menu>
</Box>
{tagCategory.tagList.length > 0 &&
tagCategory.tagList.map((tag, j) => (
<Box key={j}>
<Box sx={{ display: 'flex', marginTop: '32px' }}>
<Box
sx={{
display: 'flex',
alignItems: 'center',
background: '#283237',
color: '#ffffff',
padding: '0 0 0 12px',
borderRadius: '20px',
fontWeight: 'bold',
marginLeft: '8px',
':first-of-type': {
marginLeft: 0,
},
}}
>
{tag.name}
<IconButton
size="small"
sx={{ color: '#ffffff' }}
>
<Icon fontSize="small">close</Icon>
</IconButton>
</Box>
<Button
sx={{
display: 'flex',
alignItems: 'center',
background: '#ffffff',
color: '#000000',
padding: '0 12px',
borderRadius: '20px',
fontWeight: 'bold',
border: '#aaaaaa 1px solid',
marginLeft: '8px',
textTransform: 'none',
':hover': {
borderColor: '#1976d2',
boxShadow: '0px 0px 0px 1px #1976d2 inset',
},
':only-child': {
marginLeft: 0,
},
}}
onClick={() => {
setCategoryIndex(i)
setTagIndex(j)
setTraitsPickerOpen(true)
}}
>
Add Traits
</Button>
</Box>
{taggedList[i] && (
<Box
sx={{
display: 'flex',
flexWrap: 'wrap',
marginTop: '8px',
width: '1000px',
}}
>
{taggedList[i][j].map((trait, k) => (
<Trait key={k} {...trait} />
))}
</Box>
)}
</Box>
))}
</Box>
<Button
onClick={() => onClickToggleUntaggedComponentsBtn(i)}
sx={{
fontSize: '0.85rem',
marginTop: '40px',
color: '#005eff',
textTransform: 'none',
}}
>
{isVisibleUntaggedList[i] ? 'Hide' : 'Show'} untagged
components
</Button>
{isVisibleUntaggedList[i] && (
<Box sx={{ width: '1000px', maxWidth: '100vw' }}>
{unTaggedList[i] &&
Object.keys(unTaggedList[i]).map((key, k) => (
<Box key={k}>
{unTaggedList[i][key].length > 0 && (
<>
<Box sx={{ fontWeight: 'bold' }}>{key}</Box>
<Box
sx={{
display: 'flex',
flexDirection: 'row',
flexWrap: 'wrap',
}}
>
{unTaggedList[i][key].map((trait, l) => (
<Trait key={`${k}_${l}`} {...trait} />
))}
</Box>
</>
)}
</Box>
))}
</Box>
)}
</Box>
))}
</Box>
)}
<CreateTagCategoryModal
open={createModalOpen}
setOpen={setCreateModalOpen}
onCreate={addTagCategory}
/>
<TraitsPickerModal
title={
tagCategoryList[categoryIndex]
? `${tagCategoryList[categoryIndex].tagList[tagIndex].name} Traits`
: ''
}
open={traitsPickerOpen}
setOpen={setTraitsPickerOpen}
onPick={onClickTrait}
traitList={
taggedList[categoryIndex]
? taggedList[categoryIndex][tagIndex]
: []
}
/>
</Grid>
</Box>
</MainLayout>
)
}
export default Tags
My modalEdit
import * as React from 'react'
import {
Box,
Button,
Modal,
Typography,
IconButton,
TextField,
FormControlLabel,
Switch,
} from '#mui/material'
import { ModalBox } from 'components/Elements/Box/ModalBox'
import { useStaticTagsViewModelSWR } from '../../hooks/useStaticArtViewModelSWR'
import CloseIcon from '#mui/icons-material/Close'
import AddCircleOutlineIcon from '#mui/icons-material/AddCircleOutline'
import DeleteOutlineOutlinedIcon from '#mui/icons-material/DeleteOutlineOutlined'
export const TagCategoryEditModal = () => {
const { viewModel, setViewModel } = useStaticTagsViewModelSWR()
const [includeMetadataFlag, setIncludeMetadataFlag] =
React.useState<boolean>(true)
const modalClose = () => {
setViewModel({ ...viewModel, tagCategoryEditModal: { isOpen: false } })
}
return (
<Modal
open={viewModel.tagCategoryEditModal.isOpen}
onClose={modalClose}
aria-labelledby="modal-modal-title"
aria-describedby="modal-modal-description"
>
<>
<ModalBox
style={{ display: 'flex', flexDirection: 'column', height: 450 }}
>
<Box
sx={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
mb: 2,
}}
>
<Typography variant="h4" sx={{ fontWeight: 'bold' }} gutterBottom>
Edit Tag Category
</Typography>
<IconButton onClick={modalClose}>
<CloseIcon />
</IconButton>
</Box>
<Box sx={{ mb: 3 }}>
<Typography sx={{ mb: 1 }}>Name</Typography>
<TextField sx={{ width: '100%' }} />
</Box>
<Box sx={{ mb: 3 }}>
<Box
sx={{
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
}}
>
<Typography>Tags</Typography>
<IconButton>
<AddCircleOutlineIcon />
</IconButton>
</Box>
<Box
sx={{
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
}}
>
<TextField sx={{ width: '100%', mr: 2 }} />
<IconButton>
<DeleteOutlineOutlinedIcon />
</IconButton>
</Box>
</Box>
<Box sx={{ mb: 2 }}>
<FormControlLabel
sx={{ ml: 0 }}
value="start"
control={
<Switch
color="primary"
checked={includeMetadataFlag}
onChange={() => setIncludeMetadataFlag(!includeMetadataFlag)}
/>
}
label="Include in metadata?"
labelPlacement="start"
/>
</Box>
<Box sx={{ display: 'flex', justifyContent: 'flex-end' }}>
<Button
variant="outlined"
sx={{ color: 'black', borderColor: '#d1d8db', marginRight: 1 }}
onClick={modalClose}
>
Cancel
</Button>
<Button variant="contained">Save</Button>
</Box>
</ModalBox>
</>
</Modal>
)
}

How to center the search component in MUI AppBar?

I've been struggling to center the search component in the AppBar of Material-UI. I wanted the search bar to remain in the center. Using this code from their website. I've played around margins and justify, but I can't seem to get the correct way of doing it and remain responsive.
const Search = styled('div')(({ theme }) => ({
position: 'relative',
borderRadius: theme.shape.borderRadius,
backgroundColor: alpha(theme.palette.common.white, 0.15),
'&:hover': {
backgroundColor: alpha(theme.palette.common.white, 0.25),
},
marginLeft: 0,
width: '100%',
[theme.breakpoints.up('sm')]: {
marginLeft: theme.spacing(1),
width: 'auto',
},
}));
const SearchIconWrapper = styled('div')(({ theme }) => ({
padding: theme.spacing(0, 2),
height: '100%',
position: 'absolute',
pointerEvents: 'none',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}));
const StyledInputBase = styled(InputBase)(({ theme }) => ({
color: 'inherit',
'& .MuiInputBase-input': {
padding: theme.spacing(1, 1, 1, 0),
// vertical padding + font size from searchIcon
paddingLeft: `calc(1em + ${theme.spacing(4)})`,
transition: theme.transitions.create('width'),
width: '100%',
[theme.breakpoints.up('sm')]: {
width: '12ch',
'&:focus': {
width: '20ch',
},
},
},
}));
export default function SearchAppBar() {
return (
<Box sx={{ flexGrow: 1 }}>
<AppBar position="static">
<Toolbar>
<IconButton
size="large"
edge="start"
color="inherit"
aria-label="open drawer"
sx={{ mr: 2 }}
>
<MenuIcon />
</IconButton>
<Typography
variant="h6"
noWrap
component="div"
sx={{ flexGrow: 1, display: { xs: 'none', sm: 'block' } }}
>
MUI
</Typography>
<Search>
<SearchIconWrapper>
<SearchIcon />
</SearchIconWrapper>
<StyledInputBase
placeholder="Search…"
inputProps={{ 'aria-label': 'search' }}
/>
</Search>
</Toolbar>
</AppBar>
</Box>
);
}
How do I achieve something likes this?
Because Toolbar is a flex container, if you set its justify-content to space-between the element in the middle will be centered.
<AppBar position="static">
<Toolbar
sx={{
justifyContent: "space-between"
}}
>
{/* group IconButton and Typography in an element so there are */}
{/* only 3 children in the flex container */}
<Stack direction="row" alignItems="center">
<IconButton {...} />
<Typography {...} />
</Stack>
<Search {...} />
<IconButton {...} />
</Toolbar>
</AppBar>
Live Demo

Toolbar container to observe minWidth

I'm new to react and material UI and I've been trying to design my navbar so that my logo, search bar and drawer are in the center. I was able to get help earlier to get space between the components and have my search bar centered, but now i'm struggling to have my logo and drawer to be in the center while observing a minimum width from my searchbar.
I already tried wrapping them into a container with a specified width but they lose their alignment in the center.
here's my code:
const Search = styled('div')(({ theme }) => ({
position: 'relative',
borderRadius: 30,
backgroundColor: alpha(theme.palette.common.white, 0.15),
'&:hover': {
backgroundColor: alpha(theme.palette.common.white, 0.25),
},
// marginLeft: 10,
width: 'auto'
}));
const SearchIconWrapper = styled('div')(({ theme }) => ({
padding: theme.spacing(0, 2),
height: '100%',
position: 'absolute',
pointerEvents: 'none',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}));
const StyledInputBase = styled(InputBase)(({ theme }) => ({
color: 'inherit',
'& .MuiInputBase-input': {
padding: theme.spacing(1, 1, 1, 0),
// vertical padding + font size from searchIcon
paddingLeft: `calc(1em + ${theme.spacing(4)})`,
transition: theme.transitions.create('width'),
width: 'auto',
},
}));
export default function SearchAppBar({ search, setSearch }) {
return (
<Box sx={{ flexGrow: 1 }}>
<AppBar position="fixed" sx={{ backgroundColor: "#55597d" }}>
<Toolbar sx={{ justifyContent: "space-between" }}>
<Stack direction="row" alignItems="center">
<img
style={{ marginRight: "10px" }}
src={logo}
alt="logo"
className="logotext"
width="38"
height="38"
/>
</Stack>
<Search>
<SearchIconWrapper>
<SearchIcon />
</SearchIconWrapper>
<StyledInputBase
sx={{ width: "auto" }}
value={search}
onChange={(e) => {
setSearch(e.target.value);
}}
placeholder="Search All Games…"
inputProps={{ "aria-label": "search" }}
/>
</Search>
{/*</div>*/}
<IconButton
size="large"
edge="start"
color="inherit"
aria-label="open drawer"
sx={{ mr: 0, ml: 0 }}
>
<MenuIcon />
</IconButton>
</Toolbar>
</AppBar>
</Box>
);
}

Connect React Material UI: Mini Variant + Responsive drawer

I am new to React and Material UI and I've been trying really hard to connect Mini Variant Drawer that I have now with Responsive drawer, so when you use it on phone it changes to Responsive drawer. If anyone could help me it would mean a lot to me, I tried but I always failed.
It would also help me get the knowledge for further connections with drawers.
Here is my code that I use for Mini Variant Drawer:
import React, {useState} from 'react';
import clsx from 'clsx';
import { makeStyles } from '#material-ui/core/styles';
import CssBaseline from '#material-ui/core/CssBaseline';
import Drawer from '#material-ui/core/Drawer';
import AppBar from '#material-ui/core/AppBar';
import Toolbar from '#material-ui/core/Toolbar';
import List from '#material-ui/core/List';
import Typography from '#material-ui/core/Typography';
import Divider from '#material-ui/core/Divider';
import IconButton from '#material-ui/core/IconButton';
import MenuIcon from '#material-ui/icons/Menu';
import MenuOpenIcon from '#material-ui/icons/MenuOpen';
import { MainListItems } from './listItems';
import AccountMenu from '../../components/AccountMenu'
import ChangePasswordDialog from './ChangePasswordDialog';
const drawerWidth = 240;
const useStyles = makeStyles((theme) => ({
root: {
display: 'flex',
},
toolbar: {
paddingRight: 60,
},
toolbarIcon: {
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-end',
padding: '0 8px',
...theme.mixins.toolbar,
},
appBar: {
zIndex: theme.zIndex.drawer + 1,
transition: theme.transitions.create(['width', 'margin'], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
},
appBarShift: {
marginLeft: drawerWidth,
width: `calc(100% - ${drawerWidth}px)`,
transition: theme.transitions.create(['width', 'margin'], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.enteringScreen,
}),
},
menuButton: {
marginRight: 36,
},
menuButtonHidden: {
display: 'none',
},
mainTitle: {
flexGrow: 1,
marginLeft: 12
},
title: {
flexGrow: 1,
},
drawerPaper: {
position: 'relative',
whiteSpace: 'nowrap',
width: drawerWidth,
transition: theme.transitions.create('width', {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.enteringScreen,
}),
},
drawerPaperClose: {
overflowX: 'hidden',
transition: theme.transitions.create('width', {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
width: theme.spacing.unit * 7,
[theme.breakpoints.up('sm')]: {
width: theme.spacing.unit * 9,
},
},
appBarSpacer: theme.mixins.toolbar,
content: {
flexGrow: 1,
height: '100vh',
overflow: 'auto',
},
container: {
paddingTop: theme.spacing(4),
paddingBottom: theme.spacing(4),
},
paper: {
padding: theme.spacing(2),
display: 'flex',
overflow: 'auto',
flexDirection: 'column',
},
fixedHeight: {
height: 240,
},
}));
export default function SideMenuLayout({
drawerOpen,
setDrawerOpen,
children,
title
}) {
const classes = useStyles();
const handleDrawerOpen = () => {
setDrawerOpen(true);
};
const handleDrawerClose = () => {
setDrawerOpen(false);
};
const [changePasswordDialogOpen, setChangePasswordDialogOpen] = useState(false)
return (
<div className={classes.root}>
<CssBaseline />
<AppBar position="absolute" className={clsx(classes.appBar, drawerOpen && classes.appBarShift)}>
<Toolbar className={classes.toolbar}>
<IconButton
edge="start"
color="inherit"
aria-label="open drawer"
onClick={handleDrawerOpen}
className={clsx(classes.menuButton, drawerOpen && classes.menuButtonHidden)}
>
<MenuIcon />
</IconButton>
<Typography component="h1" variant="h6" color="inherit" noWrap className={classes.title}>
{drawerOpen ? title : `MyTitle - ${title}`}
</Typography>
<AccountMenu onChangePassword={() => setChangePasswordDialogOpen(true)} />
</Toolbar>
</AppBar>
<Drawer
variant="permanent"
classes={{
paper: clsx(classes.drawerPaper, !drawerOpen && classes.drawerPaperClose),
}}
open={drawerOpen}
>
<div className={classes.toolbarIcon}>
<Typography component="h1" variant="h6" color="inherit" noWrap className={classes.mainTitle}>
MyTitle
</Typography>
<IconButton onClick={handleDrawerClose}>
<MenuOpenIcon />
</IconButton>
</div>
<Divider />
<List><MainListItems /></List>
</Drawer>
<main className={classes.content}>
<div className={classes.appBarSpacer} />
{children}
</main>
<ChangePasswordDialog
isOpen={changePasswordDialogOpen}
setOpen={setChangePasswordDialogOpen}
/>
</div>
);
}
I too have stumbled upon some issue trying to mix together the two drawer variants, but after a couple of hours I've found a configuration that seems to work properly.
NOTE: I'm absolutely no expert in JS/CSS/HTML, I'm way more a backend guy, so this solution can certainly be optimized and modified to fit your needs.
In my case the drawer is used to select the "page" to display. So I'll start from there.
The "page" component simply contains a Router. Its contents are put inside a Box with display: flex:
const HomePage = () => {
return (
<Router>
<Box sx={{display: 'flex'}}>
<HomePageInner />
<Switch>
<Route path="/">
<p>Example</p>
</Route>
</Switch>
</Box>
</Router>
);
}
The HomePageInner is the component that renders the "menu" and appbar.
It keeps tracks of the open/closed status:
const drawerWidth = 240;
const HomePageInner = () => {
const [open, setOpen] = useState(true);
const toggleDrawer = () => setOpen(!open);
const drawer = <DrawerContents onClick={toggleDrawer} />;
return (
<>
<MainAppBar open={open} onClick={toggleDrawer}/>
<Box
component="nav"
aria-label="menu items"
>
<MobileDrawer open={open} onClose={toggleDrawer} drawer={drawer}/>
<DesktopDrawer open={open} drawer={drawer}/>
</Box>
</>
);
}
Here you can see that for clarity I have defined two distinct components for "mobile" and for "desktop". The contents of the drawer are shared and are defined in a separate DrawerContents component that is passed as a child.
There is an outer Box that wraps both Drawers. In the examples on this Box there is an sx property that specifies width: {sm: drawerWidth}, flexShrink: {sm :0}, the flexShrink should be removed, but trying to add/remove the width setting does not seem to do anything, but if something is broken for you try to set that property.
The DrawerContents is pretty simple. It places the "chevron" icon button on the top that can be used to toggle the state and shows the links:
const DrawerContents: FunctionComponent<{ onClick: () => void }> = (props) => {
return <>
<Toolbar
sx={{
display: "flex",
alignItems: "center",
justifyContent: "flex-end",
px: [1],
}}
>
<IconButton onClick={props.onClick}>
<ChevronLeftIcon/>
</IconButton>
</Toolbar>
<Divider/>
<List>
<ListItemLink primary="First" to="/first" icon={<DashboardIcon/>}/>
</List>
<List>
<ListItemLink primary="Second" to="/second" icon={<TodayIcon/>}/>
</List>
<Divider/>
<List>
<ListItemLink primary="Third" to="/third" icon={<SettingsIcon/>}/>
</List>
</>;
}
Now for the interesting part! The Drawers and the AppBar. So, the MobileDrawer is pretty much exactly the same as the temporary drawer in the "Responsive Drawer" example:
const MobileDrawer: FunctionComponent<{ container?: (() => any), open: boolean, onClose: () => void }> = (props) => {
return <Drawer
variant="temporary"
container={props.container}
open={props.open}
onClose={props.onClose}
ModalProps={{
keepMounted: true,
}}
sx={{
display: {xs: "block", sm: "none"},
"& .MuiDrawer-paper": {boxSizing: "border-box", width: drawerWidth},
}}
>
{props.children}
</Drawer>;
}
For the DesktopDrawer is mostly the same of the permanent drawer in th Responsive Drawer example however you have to modify the styling in the sx property so that it changes width when the status changes:
const DesktopDrawer: FunctionComponent<{ open: boolean }> = (props) => {
return <Drawer
variant="permanent"
sx={{
display: {xs: "none", sm: "block"},
"& .MuiDrawer-paper": {
position: "relative",
whiteSpace: "nowrap",
width: drawerWidth,
transition: theme => theme.transitions.create('width', {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.enteringScreen,
}),
boxSizing: "border-box",
...(!props.open && {
overflowX: "hidden",
transition: theme => theme.transitions.create('width', {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
width: theme => ({xs: theme.spacing(7), sm: theme.spacing(9)}),
})
},
}}
open={props.open}
>
{props.children}
</Drawer>;
}
Note that here I made ample use of the function form of the sx properties that gives you access to the theme. I derived the correct values by "mergin" the values I saw in the Mini-Variant example and the Responsive example.
And now the MainAppBar. This too is quite similar to the Responsive Drawer example, however again we have to modify the sx property:
const MainAppBar: FunctionComponent<{ open: boolean, onClick: () => void }> = (props) => {
const location = useLocation();
const headers: { [key: string]: string } = {
'/first': "First",
'/second': "Second",
'/third': "Third",
};
return <AppBar
position="fixed"
sx={{
zIndex: theme => theme.zIndex.drawer + 1,
transition: theme => theme.transitions.create(['width', 'margin'], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
...(props.open && {
ml: {sm: `${drawerWidth}px`},
width: {sm: `calc(100% - ${drawerWidth}px)`},
transition: theme => theme.transitions.create(['width', 'margin'], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.enteringScreen,
}),
})
}}
>
<Toolbar>
<IconButton
color="inherit"
aria-label="open drawer"
edge="start"
onClick={props.onClick}
sx={{mr: 2}}
>
<MenuIcon/>
</IconButton>
<Typography
component="h1"
variant="h6"
color="inherit"
noWrap
sx={{flexGrow: 1}}
>
{headers[location.pathname] || "Error"}
</Typography>
</Toolbar>
</AppBar>;
}
Note that here you want to:
remove the display: { sm: 'none' } in the sx of the IconButton to toggle the state
The width and ml settings must be included only in the open state
With all this configuration I get a drawer that for larger screen can be opened/closed like the mini variant:
But is also responsive:
PS: I didn't have the time to write a short answer, so I wrote a long one by copy&pasting my code and cutting away some stuff. If I find some time I might modify the sample code to be shorter and/or be self-contained.

popper hiding the page components

I have a page with the sidebar as the main component and the rest of the components of the page as the children, I have a popper in the appbar for a button, I want to move my child components of the sidedrawer to shift to the left of popper when it appears
I already have all the components , I just have to shift the components to the left of the popper
const drawerWidth = 240;
const styles = theme => ({
root: {
display: 'flex',
},
flex: {
flexGrow: 1,
},
appBar: {
zIndex: theme.zIndex.drawer + 1,
transition: theme.transitions.create(['width', 'margin'], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
},
appBarShift: {
marginLeft: drawerWidth,
width: `calc(100% - ${drawerWidth}px)`,
transition: theme.transitions.create(['width', 'margin'], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.enteringScreen,
}),
},
menuButton: {
marginLeft: 12,
marginRight: 25,
},
hide: {
display: 'none',
},
drawer: {
width: drawerWidth,
flexShrink: 0,
whiteSpace: 'nowrap',
},
drawerOpen: {
width: drawerWidth,
transition: theme.transitions.create('width', {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.enteringScreen,
}),
},
drawerClose: {
transition: theme.transitions.create('width', {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
overflowX: 'hidden',
width: theme.spacing.unit * 7 + 1,
[theme.breakpoints.up('sm')]: {
width: theme.spacing.unit * 9 + 1,
},
},
toolbar: {
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-end',
padding: '0 8px',
...theme.mixins.toolbar,
},
content: {
flexGrow: 1,
padding: theme.spacing.unit * 3,
},
});
class TaskListMain extends React.Component {
state = {
open: false,
popup:false,
anchorEl: null,
notifier:false
};
handleDrawerOpen = () => {
this.setState({ open: true });
};
handleDrawerClose = () => {
this.setState({ open: false });
};
handleToggle = event => {
const { currentTarget } = event;
this.setState(state => ({
anchorEl: currentTarget,
popup: !state.popup,
}));
}
handleIcon = event => {
const { currentTarget } = event;
this.setState(state => ({
anchorEl: currentTarget,
notifier: !state.notifier,
}));
}
render() {
const { classes, theme } = this.props;
const { anchorEl, popup, notifier } = this.state;
let otherTask;
let notify;
if(this.state.popup){
otherTask=<OtherTaskPaper/>
}
if(this.state.notifier){
notify=<NotificationPaper/>
}
return (
<div className={classes.root}>
<CssBaseline/>
<AppBar
position="fixed"
className={classNames(classes.appBar, {
[classes.appBarShift]: this.state.open,
})}style={{ background: '#00050f' }}
>
<Toolbar disableGutters={!this.state.open}>
<IconButton
color="inherit"
aria-label="Open drawer"
onClick={this.handleDrawerOpen}
className={classNames(classes.menuButton, {
[classes.hide]: this.state.open,
})}
>
<MenuIcon />
</IconButton>
<Typography variant="title" color="inherit" className={classes.flex}>
<img src="../../logo.jpg" alt="drawer" height="30px"/></Typography>
<Button color="inherit" onClick={this.handleToggle}>
Other Tasks
</Button>
<Popper open={popup} anchorEl={anchorEl}>
<Paper>
{otherTask}
</Paper>
</Popper>
<IconButton className={classes.menuButton} color="inherit" aria-label="Menu" onClick={this.handleIcon}>
<Badge badgeContent={2} style={{borderColor:""}}>
<NotificationsIcon />
</Badge>
</IconButton>
<Popper open={notifier} anchorEl={anchorEl}>
<Paper>
{notify}
</Paper>
</Popper>
</Toolbar>
</AppBar>
<Drawer
variant="permanent"
className={classNames(classes.drawer, {
[classes.drawerOpen]: this.state.open,
[classes.drawerClose]: !this.state.open,
})}
classes={{
paper: classNames({
[classes.drawerOpen]: this.state.open,
[classes.drawerClose]: !this.state.open,
}),
}}
open={this.state.open}
>
<div className={classes.toolbar}>
<IconButton onClick={this.handleDrawerClose}>
{theme.direction === 'rtl' ? <ChevronRightIcon /> : <ChevronLeftIcon />}
</IconButton>
</div>
<Divider />
<List>
<ListItem button>
<ListItemIcon>
<AssignmentTurnedIn />
</ListItemIcon>
<ListItemText primary="Tasks" />
</ListItem>
<ListItem button>
<ListItemIcon>
<DateRange/>
</ListItemIcon>
<ListItemText primary="TimeSheet" />
</ListItem>
<ListItem button>
<ListItemIcon>
<Receipt/>
</ListItemIcon>
<ListItemText primary="Reports" />
</ListItem>
</List>
</Drawer>
<main className={classes.content}>
<div className={classes.toolbar} />
{this.props.children}
</main>
</div>
);
}
}
TaskListMain.propTypes = {
classes: PropTypes.object.isRequired,
theme: PropTypes.object.isRequired,
};
//child components
import TaskCardComponent from './TaskCardComponent'
import TaskList from './TaskList'
const styles = theme => ({
root: {
...theme.mixins.gutters(),
paddingTop: theme.spacing.unit * 5,
paddingBottom: theme.spacing.unit * 5,
background: "grey"
},
});
function TaskListComponents(props) {
const { classes } = props;
return (
<div>
<Paper className={classes.root} elevation={1}>
<TaskCardComponent/>
<TaskList/>
</Paper>
</div>
);
}
TaskListComponents.propTypes = {
classes: PropTypes.object.isRequired,
};
export default withStyles(styles)(TaskListComponents);
Write style for shifting the contents to one side like me did for the side drawer
contentShift: {
marginRight: popperwidth, width: `calc(100% - ${popperwidth}px)`, transition:
theme.transitions.create(['width', 'margin'], { easing:
theme.transitions.easing.sharp, duration: theme.transitions.duration.enteringScreen,
}),
},
state = {
open: false,
popup:false,
anchorEl: null,
notifier:false,
};
handleToggle =
event => {
const { currentTarget } = event; this.setState(state => ({ anchorEl:
currentTarget, popup: !state.popup, }));
}
------------------------------------------------------------------------

Resources