I have an appBar and the homepage would appear behind the appbar. I wanted it to appear below it. This is what it looks like:
The AppBar codes:
const Header = () => {
const [value, setValue] = React.useState(0);
const handleChange = (event, newValue) => {
setValue(newValue);
};
//Breakpoints
const theme = useTheme();
const isMatch = useMediaQuery(theme.breakpoints.down("md"));
return (
<div>
<AppBar>
<Toolbar>
{/* //or just change this typography to an icon or picture */}
<Typography>Website</Typography>
{isMatch ? (
<h1>
<DrawerComponent />
</h1>
) : (
<Tabs
value={value}
indicatorColor="secondary"
onChange={handleChange}
aria-label="simple tabs example"
>
<Tab disableRipple label="Homepage" to="/" component={Link} />
<Tab disableRipple label="Login" to="/login" component={Link} />
<Tab disableRipple label="Settings" />
<Tab disableRipple label="Sample1" />
<Tab disableRipple label="Sample2" />
<Tab disableRipple label="Sample3" />
</Tabs>
)}
</Toolbar>
</AppBar>
</div>
);
};
export default Header;
I need to put a <br/> just to see the homepage:
const Homepage = (props) => {
return (
<section>
<br />
<h1>Homepage</h1>
</section>
);
};
export default Homepage;
And I have this drawerComponent for small screen sizes, it even got worse, you won't be able to see any message anymore not unless there will be a lot of <br/> before the message.
const DrawerComponent = () => {
const useStyles = makeStyles((theme) => ({
drawerContainer: {},
iconButtonContainer: {
marginLeft: "auto",
color: "white",
},
menuIconToggle: {
fontSize: "3rem",
},
link: {
textDecoration: "none",
},
}));
const [openDrawer, setOpenDrawer] = useState(false);
//Css
const classes = useStyles();
return (
<div>
<Drawer
anchor="left"
classes={{ paper: classes.drawerContainer }}
onClose={() => setOpenDrawer(false)}
open={openDrawer}
onOpen={() => setOpenDrawer(true)}
>
<List className={classes.link}>
<Link to="/">
<ListItem divider button onClick={() => setOpenDrawer(false)}>
<ListItemIcon>
<ListItemText> Homepage</ListItemText>
</ListItemIcon>
</ListItem>
</Link>
<Link to="/login">
<ListItem divider button onClick={() => setOpenDrawer(false)}>
<ListItemIcon>
<ListItemText> Login</ListItemText>
</ListItemIcon>
</ListItem>
</Link>
<ListItem divider button onClick={() => setOpenDrawer(false)}>
<ListItemIcon>
<ListItemText>Sample</ListItemText>
</ListItemIcon>
</ListItem>
<ListItem divider button onClick={() => setOpenDrawer(false)}>
<ListItemIcon>
<ListItemText> Sample</ListItemText>
</ListItemIcon>
</ListItem>
</List>
</Drawer>
<IconButton
edge="end"
className={classes.iconButtonContainer}
onClick={() => setOpenDrawer(!openDrawer)}
disableRipple
>
<MenuIcon className={classes.menuIconToggle} />
</IconButton>
</div>
);
};
export default DrawerComponent;
A way around this would be to add a margin-top or a padding-top to your homepage component equal to the height of the appbar.
Yet, a better approach would be ro use the following CSS properties on your appBar.
.app-bar {
position: sticky;
top: 0;
}
This will make your appbar stick to the top and will automatically adjust the height of its following DOM elements.
This post may answer your question: Creating a navbar with material-ui
You can either try:
Using CSS to implement padding-top (use "em" instead of "px" for a responsive padding height)
Reorganising your React components, making sure that the header (appbar) is not in the page, but rather a component at the same level (refer to the post linked above)
Related
Here is my *Navbar
import React, { useState } from "react";
import { Layout, Menu } from "antd";
import {
MenuUnfoldOutlined,
MenuFoldOutlined,
UserOutlined,
VideoCameraOutlined,
UploadOutlined,
} from "#ant-design/icons";
import { Link } from "react-router-dom";
const { Header, Sider, Content } = Layout;
const Navbar = (props) => {
const [collapsed, setCllapsed] = useState(false);
const toggle = () => {
setCllapsed(!collapsed);
};
return (
<div>
<Layout id="components-layout-demo-custom-trigger">
<Sider trigger={null} collapsible collapsed={collapsed}>
<div className="logo" />
<Menu theme="dark" mode="inline" defaultSelectedKeys={["1"]}>
<Menu.Item key="1" icon={<UserOutlined />}>
<Link style={{ textDecoration: "none" }} to="/">
Dashboard
</Link>
</Menu.Item>
<Menu.Item key="2" icon={<UserOutlined />}>
<Link style={{ textDecoration: "none" }} to="/groups">
Groups
</Link>
</Menu.Item>
<Menu.Item key="3" icon={<VideoCameraOutlined />}>
<Link style={{ textDecoration: "none" }} to="/tutors">
Tutors
</Link>
</Menu.Item>
<Menu.Item key="4" icon={<UserOutlined />}>
<Link style={{ textDecoration: "none" }} to="/students">
Students
</Link>
</Menu.Item>
</Menu>
</Sider>
<Layout className="site-layout">
<Header className="site-layout-background" style={{ padding: 0 }}>
{React.createElement(
collapsed ? MenuUnfoldOutlined : MenuFoldOutlined,
{
className: "trigger",
onClick: toggle,
}
)}
</Header>
<Content
className="site-layout-background"
style={{
margin: "24px 16px",
padding: 24,
minHeight: 280,
}}
>
{props.children}
</Content>
</Layout>
</Layout>
</div>
);
};
export default Navbar;
In this example I was using <Menu.Item></Menu.Item> component from antd . But it stopped working when clicked once. It changed onclick function. After adding from react-router-dom, I am having trouble with SelectedKeys of Antdesign's Layout. Before Link component of react-router-dom selectedKeys was working when you click one time, but after adding Link component, SelectedKeys is working when double clicked. How can I solve it? I need this should work once. Anyone can help me?
I have this appBar with a tab and an IconButton. I wanted to put the IconButton at the right side, but float: right does not work.
This is what it looks like:
I have also recreated this in code sandbox:
https://codesandbox.io/s/suspicious-morning-6pf26?file=/src/App.js:234-2709
Below are the entire codes:
export default function App() {
const [anchorEl, setAnchorEl] = useState(null);
const isMenuOpen = Boolean(anchorEl);
const handleProfileMenuOpen = (event) => {
setAnchorEl(event.currentTarget);
};
const handleMenuClose = () => {
setAnchorEl(null);
};
const menuId = "primary-search-account-menu";
const renderMenu = (
<Menu
anchorEl={anchorEl}
anchorOrigin={{ vertical: "top", horizontal: "right" }}
id={menuId}
keepMounted
transformOrigin={{ vertical: "top", horizontal: "right" }}
open={isMenuOpen}
onClose={handleMenuClose}
>
<MenuItem>Profile</MenuItem>
<MenuItem>Log Out</MenuItem>
</Menu>
);
const [value, setValue] = useState(0);
const [open, setOpen] = useState(false);
const handleChange = (event, newValue) => {
setValue(newValue);
};
return (
<div className="App">
<AppBar
style={{
position: "sticky",
top: "0"
}}
>
<Toolbar variant="dense">
<div>
<>
<Grid
container
direction="row"
justifyContent="space-between"
alignItems="center"
>
<Grid item>
<Tabs
centered
value={value}
fullWidth={true}
indicatorColor="primary"
onChange={handleChange}
aria-label="simple tabs example"
variant="fullWidth"
>
<Tab disableRipple label="Homepage" to="/" />
<Tab disableRipple label="About" to="/about" />
<Tab disableRipple label="Admin" to="/admin" />
</Tabs>
</Grid>
<Grid item alignItems="flex-end" style={{ float: "right" }}>
<IconButton
edge="end"
right
aria-label="account of current user"
aria-controls={menuId}
aria-haspopup="true"
onClick={handleProfileMenuOpen}
color="inherit"
>
<AccountCircle />
</IconButton>
</Grid>
</Grid>
</>
</div>
</Toolbar>
</AppBar>
{renderMenu}
</div>
);
}
How should I love the AccountCircle at the right edge of the screen?
It looks like your snippet in the sandbox is working correctly. Can you double-check that there are no differences between the sandbox and your actual local code?
Looking at your code snippet, it looks like "float: right" is passed to the parent of IconButton, instead of iconbutton itself. Is it fixed (on your local environment, since code snippet appears to work) when you move the style to icon button?
I'm getting some weird problems with the Material-UI drawer component. It scrolls horizontal on my iPad, compared to my MacBook and Android Phone, which actually scroll vertically..
Result on MacBook (how it's supposed to be..):
As you can see, it scrolls down and it overflows vertically, just like on my phone
Result on iPad (how it's not supposed to be..):
However, as you can see.. on my iPad it overflows horizontally..?
The code for the drawer:
<Drawer
anchor="right"
open={open}
onClose={HandleClose}
classes={{ paper: classes.drawerPaper }}>
<Toolbar />
<Typography className={classes.title}>Selecteer actieve regio's</Typography>
<FormControl component="fieldset" className={classes.formControl}>
<FormGroup>
{regios.map(regio => (
<FormControlLabel
control={
<Checkbox
checked={geselecteerdeRegios.includes(regio.ID)}
onChange={e => handleRegioChange(regio.ID, e.target.checked)}
/>
}
name={regio.Naam}
label={regio.Naam}
key={regio.Naam}
/>
))}
</FormGroup>
</FormControl>
</Drawer>
And these are the styles for the drawer
drawerPaper: {
width: 320
},
Why is this happening? And how can I prevent this and only allow vertical scroll/overflow instead of horizontal..?
Edit: codesandbox:
https://codesandbox.io/s/elated-khayyam-t6klg?file=/src/Drawer.js
Thanks in advance! :-)
This appears to be related to the default styles on FormGroup:
/* Styles applied to the root element. */
root: {
display: 'flex',
flexDirection: 'column',
flexWrap: 'wrap',
},
You can override this to nowrap to prevent the wrapping (as shown below with classes.formGroup in a modified version of your sandbox). I have not taken the time to understand why this behaves differently on iPads than on other devices.
import React from "react";
import {
Drawer,
FormControl,
FormGroup,
FormControlLabel,
Checkbox,
Toolbar,
Typography,
makeStyles
} from "#material-ui/core";
const useStyles = makeStyles(theme => ({
drawerPaper: {
width: 320
},
formGroup: {
flexWrap: "nowrap"
}
}));
export default () => {
let items = [];
for (let i = 0; i < 25; i++) items.push("foobarfazboo" + i);
const classes = useStyles();
return (
<Drawer anchor="right" open={true} classes={{ paper: classes.drawerPaper }}>
<Toolbar />
<Typography className={classes.title}>
Selecteer actieve regio's
</Typography>
<FormControl component="fieldset" className={classes.formControl}>
<FormGroup className={classes.formGroup}>
{items.map(item => (
<FormControlLabel
control={<Checkbox />}
name={item}
label={item}
key={item}
/>
))}
</FormGroup>
</FormControl>
</Drawer>
);
};
Unfortunately, this still leaves some odd behavior on the iPad. It seems to try to fit all of the content in the Drawer without scrolling (e.g. if I add 100 items instead of 25, they still are all forced to fit and become an unreadable, squished mess).
I don't fully understand what is happening, but it appears to be an interplay between the height and display: flex on the Drawer Paper. Changing the display from flex to block fixes this. I don't see any obvious new problems caused by this, but I haven't taken a lot of time to look at what other effects this change causes. With this change, the above "nowrap" change appears unnecessary.
import React from "react";
import {
Drawer,
FormControl,
FormGroup,
FormControlLabel,
Checkbox,
Toolbar,
Typography,
makeStyles
} from "#material-ui/core";
const useStyles = makeStyles(theme => ({
drawerPaper: {
width: 320,
display: "block"
}
}));
export default () => {
let items = [];
for (let i = 0; i < 25; i++) items.push("foobarfazboo" + i);
const classes = useStyles();
return (
<Drawer anchor="right" open={true} classes={{ paper: classes.drawerPaper }}>
<Toolbar />
<Typography className={classes.title}>
Selecteer actieve regio's
</Typography>
<FormControl component="fieldset" className={classes.formControl}>
<FormGroup>
{items.map(item => (
<FormControlLabel
control={<Checkbox />}
name={item}
label={item}
key={item}
/>
))}
</FormGroup>
</FormControl>
</Drawer>
);
};
I have a react application using material-ui to create tabs.
<div className={classes.root}>
<AppBar position="static">
<Tabs value={value} onChange={handleChange}>
<Tab label="Item One" />
<Tab label="Item Two" />
<Tab label="Item Three" />
</Tabs>
</AppBar>
{value === 0 && <TabContainer id={1}>Item One</TabContainer>}
{value === 1 && <TabContainer id={2}>Item Two</TabContainer>}
{value === 2 && <TabContainer id={3}>Item Three</TabContainer>}
</div>
The TabContainer is a functional component and does some heavy computation.
Is it possible to prevent TabContainer from re-rendering when switching between tabs?
Update:
Check my answer for a solution with React functional components and css classes.
In order to prevent TabContainer from re-rendering. You have to
Render all TabContainer data at once instead of rendering based on value.
You have to play with CSS and have to display only that tab which is currently active.
Also you can make your component as PureComponent or you can override shouldComponentUpdate() lifecycle method to stop extra re-rendering of your react component.
Update/Partial Solution:
With the below code (based on Rahul Jain's answer) using css classes to display the active TabContainer, the memoized functions seems to be really memoized.
const useTabContainerStyles = makeStyles((theme: Theme) => createStyles({
root: {
padding: 8 * 3
},
tabcontainerInActive: {
display: "none"
}
})
);
function TabContainer(props: TabContainerProps) {
const styles = useTabContainerStyles({});
console.log("In TabContainer");
const doubleValue = useMemo(() => double(props.id), [props.id]);
return (
<Typography
id={props.id.toString()}
component="div"
className={classnames(styles.root, {
[styles.tabcontainerInActive]: !props.active
})}
>
{props.children + " " + doubleValue}
</Typography>
);
}
export default function SimpleTabs() {
const classes = useStyles({});
const [selectedTab, setSelectedTab] = React.useState(0);
function handleChange(event: React.ChangeEvent<{}>, newValue: number) {
setSelectedTab(newValue);
}
return (
<div className={classes.root}>
<AppBar position="static">
<Tabs value={selectedTab} onChange={handleChange}>
<Tab label="Item One" />
<Tab label="Item Two" />
<Tab label="Item Three" />
</Tabs>
</AppBar>
{/* */}
<TabContainer id={0} active={selectedTab === 0}>
Item One
</TabContainer>
<TabContainer id={1} active={selectedTab === 1}>
Item Two
</TabContainer>
<TabContainer id={2} active={selectedTab === 2}>
Item Three
</TabContainer>
</div>
);
}
I'm creating a web application using Material-UI. The main page is divided in 3 grids, each with a height of 500px. I wanted to display a drawer with some options of actions inside the middle grid. Is that possible? The way I have it so far I can only display it in relation to the whole screen.
Here's my main component with the grid elements:
import React, { Fragment } from 'react';
import Grid from '#material-ui/core/Grid';
import Paper from '#material-ui/core/Paper';
const style = {
Paper: {
padding: 10,
marginTop: 5,
marginBottom: 5,
height: 500,
overflowY: 'auto',
position: 'relative',
},
Fab: {
position: 'absolute',
top: 5,
left: 5
},
}
export default () => {
return (
<Fragment>
<Grid container spacing={1}>
<Grid item xs={12} sm={4} md={4} lg={2}>
<Paper style={style.Paper}>
</Paper>
</Grid>
<Grid item xs={12} sm={8} md={8} lg={4}>
<Paper style={style.Paper}>
<ToolbarDrawer />
</Paper>
</Grid>
<Grid item xs={12} sm={12} md={12} lg={6}>
<Paper style={style.Paper}>
</Paper>
</Grid>
</Grid>
</Fragment>
);
}
Here's my drawer component which I would like to be displayed nested inside the middle grid:
javascript
import React, { Fragment } from 'react';
import IconButton from '#material-ui/core/IconButton';
import ChevronRightIcon from '#material-ui/icons/ChevronRight';
import ChevronLeftIcon from '#material-ui/icons/ChevronLeft';
import InboxIcon from '#material-ui/icons/MoveToInbox';
import MailIcon from '#material-ui/icons/Mail';
import { makeStyles } from '#material-ui/core/styles';
import clsx from 'clsx';
import Drawer from '#material-ui/core/Drawer';
import Divider from '#material-ui/core/Divider';
import ListItem from '#material-ui/core/ListItem';
import ListItemIcon from '#material-ui/core/ListItemIcon';
import ListItemText from '#material-ui/core/ListItemText';
import List from '#material-ui/core/List';
import CssBaseline from '#material-ui/core/CssBaseline';
const drawerWidth = 240;
const useStyles = makeStyles(theme => ({
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(6) + 1,
[theme.breakpoints.up('sm')]: {
width: theme.spacing(7) + 1,
},
},
toolbar: {
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-end',
padding: theme.spacing(0, 1),
...theme.mixins.toolbar,
}
}));
export default () => {
const classes = useStyles();
const [open, setOpen] = React.useState(false);
function handleToolbarClose() {
setOpen(!open);
}
return (
<Fragment>
<CssBaseline />
<Drawer
anchor="right"
variant="permanent"
className={clsx(classes.drawer, {
[classes.drawerOpen]: open,
[classes.drawerClose]: !open,
})}
classes={{
paper: clsx({
[classes.drawerOpen]: open,
[classes.drawerClose]: !open,
}),
}}
open={open}
>
<div className={classes.toolbar}>
<IconButton onClick={handleToolbarClose}>
{(open) ? <ChevronRightIcon /> : <ChevronLeftIcon />}
</IconButton>
</div>
<Divider />
<List>
{['Inbox', 'Starred', 'Send email', 'Drafts'].map((text, index) => (
<ListItem button key={text}>
<ListItemIcon>{index % 2 === 0 ? <InboxIcon /> : <MailIcon />}</ListItemIcon>
<ListItemText primary={text} />
</ListItem>
))}
</List>
<Divider />
<List>
{['All mail', 'Trash', 'Spam'].map((text, index) => (
<ListItem button key={text}>
<ListItemIcon>{index % 2 === 0 ? <InboxIcon /> : <MailIcon />}</ListItemIcon>
<ListItemText primary={text} />
</ListItem>
))}
</List>
</Drawer>
</Fragment>
)
}
It looks like you're going to have to build a makeshift drawer component for this. Here is an example you could use to get started:
Live demo can be found here
MakeshiftDrawer using List component and Slide transition component:
export default function MakeshiftDrawer({ open }) {
const classes = useStyles();
const [selectedIndex, setSelectedIndex] = React.useState(1);
function handleListItemClick(event, index) {
setSelectedIndex(index);
}
return (
<Slide direction="right" in={open} mountOnEnter unmountOnExit>
<div className={classes.root}>
<List component="nav" aria-label="main mailbox folders">
<ListItem
button
selected={selectedIndex === 0}
onClick={event => handleListItemClick(event, 0)}
>
<ListItemIcon>
<InboxIcon />
</ListItemIcon>
<ListItemText primary="Inbox" />
</ListItem>
<ListItem
button
selected={selectedIndex === 1}
onClick={event => handleListItemClick(event, 1)}
>
<ListItemIcon>
<DraftsIcon />
</ListItemIcon>
<ListItemText primary="Drafts" />
</ListItem>
</List>
<Divider />
<List component="nav" aria-label="secondary mailbox folder">
<ListItem
button
selected={selectedIndex === 2}
onClick={event => handleListItemClick(event, 2)}
>
<ListItemText primary="Trash" />
</ListItem>
<ListItem
button
selected={selectedIndex === 3}
onClick={event => handleListItemClick(event, 3)}
>
<ListItemText primary="Spam" />
</ListItem>
</List>
</div>
</Slide>
);
}
MakeshiftDrawer in use:
function App() {
const [isOpen, setIsOpen] = useState(true);
const toggle = () => {
setIsOpen(!isOpen);
}
return (
<div>
<Grid container direction="row" style={topGridStyle}>
</Grid>
<Grid container direction="row" style={midGridStyle}>
<Grid item>
<Button variant="contained" color="primary" onClick={toggle}>Toggle</Button>
<MakeshiftDrawer open={isOpen} />
</Grid>
</Grid>
<Grid container direction="row" style={botGridStyle}>
</Grid>
</div>
);
}
Rendered: