Material-UI ListItem "flex-start" not working - reactjs

I am using the Material-UI List item (https://material-ui.com/components/lists/)
And I would like to "left-justify" my items in my list item to the left. Apparantly I can do this with the flex property "flex-start" which I can set on the alignItems property of a ListItem like so :
<List>
<ListItem alignItems="flex-start">
<ListItemText primary={"First Element "} />
<ListItemText primary={" Second Element"} />
</ListItem>
</List>
However this does not seem to work as they are evenly spaced out when I see it in the browser.
Am I doing something wrong?

Try this...
import { makeStyles } from '#material-ui/core/styles';
const useStyles = makeStyles({
root: {
display:'flex',
alignItems:'flex-start',
flexDirection: "row"
},
});
export default function MyComponent(props) {
const classes = useStyles(props);
return (
<List className={classes.root}>
<ListItem>
<ListItemText primary={"First Element "} />
</ListItem>
<ListItem>
<ListItemText primary={"Second Element "} />
</ListItem>
</List>
);
}

ListItemText has default flex: 1 1 auto; style. You need to change it like:
import React from "react";
import { makeStyles } from "#material-ui/core/styles";
import { List, ListItem, ListItemText } from "#material-ui/core";
const useStyles = makeStyles((theme) => ({
root: {
"& .MuiListItemText-root": {
flex: "initial"
}
}
}));
export default function CustomStyles() {
const classes = useStyles();
return (
<List classes={classes}>
<ListItem alignItems="flex-start">
<ListItemText primary={"First Element "} />
<ListItemText primary={" Second Element"} />
</ListItem>
</List>
);
}

Related

Share values and events across different components in React

I am trying to build a dashboard app with basic AppBar and a drawer based on this Demo
https://codesandbox.io/s/nj3u0q?file=/demo.tsx
But in this Demo, AppBar and Drawer and the Main content are all in a single page.
But I made it as separate components and did the layout like this
RootContainer.tsx
import * as React from 'react';
import { styled, useTheme } from '#mui/material/styles';
import Box from '#mui/material/Box';
import CssBaseline from '#mui/material/CssBaseline';
import MuiAppBar, { AppBarProps as MuiAppBarProps } from '#mui/material/AppBar';
import Drawer from './Drawer';
import AppBar from './AppBar';
import Main from './MainContent';
export default function PersistentDrawerLeft() {
const [open, setOpen] = React.useState(false);
return (
<Box sx={{ display: 'flex' }}>
<CssBaseline />
<AppBar />
<Drawer />
<Main />
</Box>
);
}
Appbar.tsx
import * as React from 'react';
import { styled, useTheme } from '#mui/material/styles';
import MuiAppBar, { AppBarProps as MuiAppBarProps } from '#mui/material/AppBar';
import Toolbar from '#mui/material/Toolbar';
import Typography from '#mui/material/Typography';
import IconButton from '#mui/material/IconButton';
import MenuIcon from '#mui/icons-material/Menu';
const drawerWidth = 240;
interface AppBarProps extends MuiAppBarProps {
open?: boolean;
}
const AppBar = styled(MuiAppBar, {
shouldForwardProp: (prop) => prop !== 'open',
})<AppBarProps>(({ theme, open }) => ({
transition: theme.transitions.create(['margin', 'width'], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
...(open && {
width: `calc(100% - ${drawerWidth}px)`,
marginLeft: `${drawerWidth}px`,
transition: theme.transitions.create(['margin', 'width'], {
easing: theme.transitions.easing.easeOut,
duration: theme.transitions.duration.enteringScreen,
}),
}),
}));
export default function AppBar() {
const theme = useTheme();
const [open, setOpen] = React.useState(false);
const handleDrawerOpen = () => {
setOpen(true);
};
return (
<AppBar position="fixed" style={{background:'#002a5e'}} open={open}>
<Toolbar>
<IconButton
color="inherit"
aria-label="open drawer"
onClick={handleDrawerOpen}
edge="start"
sx={{ mr: 2, ...(open && { display: 'none' }) }}
>
<MenuIcon />
</IconButton>
<Typography variant="h6" noWrap component="div">
Test
</Typography>
</Toolbar>
</AppBar>
);
}
But the problem is when I click the sandwich button on the appbar, it reduces its width to show the Drawer, but the drawer not at all showing.
Drawer.tsx
import * as React from "react";
import { styled, useTheme } from "#mui/material/styles";
import Drawer from "#mui/material/Drawer";
import List from "#mui/material/List";
import Divider from "#mui/material/Divider";
import IconButton from "#mui/material/IconButton";
import ChevronLeftIcon from "#mui/icons-material/ChevronLeft";
import ChevronRightIcon from "#mui/icons-material/ChevronRight";
import ListItem from "#mui/material/ListItem";
import ListItemButton from "#mui/material/ListItemButton";
import ListItemIcon from "#mui/material/ListItemIcon";
import ListItemText from "#mui/material/ListItemText";
import InboxIcon from "#mui/icons-material/MoveToInbox";
import MailIcon from "#mui/icons-material/Mail";
const drawerWidth = 240;
const DrawerHeader = styled("div")(({ theme }) => ({
display: "flex",
alignItems: "center",
padding: theme.spacing(0, 1),
// necessary for content to be below app bar
...theme.mixins.toolbar,
justifyContent: "flex-end",
}));
export default function Drawer() {
const theme = useTheme();
const [open, setOpen] = React.useState(false);
const handleDrawerOpen = () => {
setOpen(true);
};
const handleDrawerClose = () => {
setOpen(false);
};
return (
<Drawer
sx={{
width: drawerWidth,
flexShrink: 0,
"& .MuiDrawer-paper": {
width: drawerWidth,
boxSizing: "border-box",
},
}}
variant="persistent"
anchor="left"
open={open}
>
<DrawerHeader>
<IconButton onClick={handleDrawerClose}>
{theme.direction === "ltr" ? (
<ChevronLeftIcon />
) : (
<ChevronRightIcon />
)}
</IconButton>
</DrawerHeader>
<Divider />
<List>
{["Manage Recipe", "Reports", "Settings"].map((text, index) => (
<ListItem key={text} disablePadding>
<ListItemButton>
<ListItemIcon>
{index % 2 === 0 ? <InboxIcon /> : <MailIcon />}
</ListItemIcon>
<ListItemText primary={text} />
</ListItemButton>
</ListItem>
))}
</List>
</Drawer>
);
}
I guess this problem is because the open constant is not getting updated into the drawer component. I am pretty new or only fews days of experience in react. Please help how can share these click event and constant in appbar to the drawer
It looks like with the separation, each component is having their own open state and handling functions, therefore the behavior is not shared.
Try pass these as props from the parent to communicate with children components.
React document about: Passing Props to a Component
There seems to be already a open state in the parent component RootContainer, so perhaps this state and handlers can be passed as props to the components, such as below example.
Over simplified example from forked demo: codesandbox
Define state and handler in the parent component, and pass these down to the children:
RootContainer.tsx
export default function PersistentDrawerLeft() {
const [open, setOpen] = React.useState(false);
const handleDrawerOpen = () => {
setOpen(true);
};
const handleDrawerClose = () => {
setOpen(false);
};
return (
<Box sx={{ display: "flex" }}>
<CssBaseline />
<AppBar open={open} onDrawerOpen={handleDrawerOpen} />
<Drawer open={open} onDrawerClose={handleDrawerClose} />
<Main />
</Box>
);
}
Have each child component use the open state and handlers from the props, instead of their own, so that they share the same state:
Appbar.tsx
export default function AppBar({ open, onDrawerOpen }) {
const theme = useTheme();
return (
<AppBar position="fixed" style={{ background: "#002a5e" }} open={open}>
<Toolbar>
<IconButton
color="inherit"
aria-label="open drawer"
onClick={onDrawerOpen}
edge="start"
sx={{ mr: 2, ...(open && { display: "none" }) }}
>
<MenuIcon />
</IconButton>
<Typography variant="h6" noWrap component="div">
Test
</Typography>
</Toolbar>
</AppBar>
);
}
Drawer.tsx
export default function Drawer({ open, onDrawerClose }) {
const theme = useTheme();
return (
<Drawer
sx={{
width: drawerWidth,
flexShrink: 0,
"& .MuiDrawer-paper": {
width: drawerWidth,
boxSizing: "border-box"
}
}}
variant="persistent"
anchor="left"
open={open}
>
<DrawerHeader>
<IconButton onClick={onDrawerClose}>
{theme.direction === "ltr" ? (
<ChevronLeftIcon />
) : (
<ChevronRightIcon />
)}
</IconButton>
</DrawerHeader>
<Divider />
<List>
{["Manage Recipe", "Reports", "Settings"].map((text, index) => (
<ListItem key={text} disablePadding>
<ListItemButton>
<ListItemIcon>
{index % 2 === 0 ? <InboxIcon /> : <MailIcon />}
</ListItemIcon>
<ListItemText primary={text} />
</ListItemButton>
</ListItem>
))}
</List>
</Drawer>
);
}
Hope this will help.

Change style of children in MaterialUI only in one element

I am using <ListItemIcon/> from Material-UI. In one component I use two different Icons and I want them to be different sizes and other styles in general. <ListItemIcon/> is build with svg which has class name MuiSvgIcon-root - this is where I should change the fontSize of Icon. I dont know how to do it. When I do
const useStyles = makeStyles({
root:{
fontSize: "2rem"
}
},{name: 'MuiSvgIcon'});
It changes setting for every Icon size in my project.
My component
function Section(props) {
const classes = useStyles();
const { title, listInfo, icon, gridSize } = props;
return (
<List>
<ListItem>
<ListItemIcon >{icon}</ListItemIcon> //here icon bigger
<ListItemText primary={title} />
</ListItem>
<Divider variant="middle" />
<Grid container>
{listInfo.map((item, index) => {
return (
<Grid item xs={gridSize}>
<ListItem key={index}>
<ListItemIcon>
<Brightness1Icon /> //here icon smaller
</ListItemIcon>
<ListItemText
primary={item.primaryText}
secondary={item.secondaryText}
/>
</ListItem>
</Grid>
);
})}
</Grid>
</List>
);
}
By specifying name: 'MuiSvgIcon' in your makeStyles call, you are causing Material-UI to override the global MuiSvgIcon-root class. If you use the name option in makeStyles, you should never give it a Mui name since these are treated differently and intended for use within the library code.
Below is one way to customize the icon size:
const BiggerListItemIcon = withStyles({
root: {
"& .MuiSvgIcon-root": { fontSize: "2em" }
}
})(ListItemIcon);
This can then be used as in the following example:
import React from "react";
import { makeStyles, withStyles } from "#material-ui/core/styles";
import List from "#material-ui/core/List";
import ListItem from "#material-ui/core/ListItem";
import ListItemIcon from "#material-ui/core/ListItemIcon";
import ListItemText from "#material-ui/core/ListItemText";
import InboxIcon from "#material-ui/icons/Inbox";
import DraftsIcon from "#material-ui/icons/Drafts";
const useStyles = makeStyles(theme => ({
root: {
width: "100%",
maxWidth: 360,
backgroundColor: theme.palette.background.paper
}
}));
const BiggerListItemIcon = withStyles({
root: {
"& .MuiSvgIcon-root": { fontSize: "2em" }
}
})(ListItemIcon);
export default function SimpleList() {
const classes = useStyles();
return (
<div className={classes.root}>
<List component="nav" aria-label="main mailbox folders">
<ListItem button>
<ListItemIcon>
<InboxIcon />
</ListItemIcon>
<ListItemText primary="Inbox" />
</ListItem>
<ListItem button>
<BiggerListItemIcon>
<DraftsIcon />
</BiggerListItemIcon>
<ListItemText primary="Drafts" />
</ListItem>
</List>
</div>
);
}
There are many ways to do styling with material-ui, I won't say this is the best way, but personally it is my own favorite way
import { styled } from "#material-ui";
const Section = () => {
return (
...
<SmallerListItemIcon>
<Brightness1Icon />
</SmallerListItemIcon>
...
)
}
const SmallerListItemIcon = styled(ListItemIcon)({
fontSize: "<your size here>"
});

Horizontally Align Primary/Secondary ListItemText, Material UI?

I want the primary and secondary text within a ListItemText to both be center-aligned, unlike the question here.
However, after trying alignItems, alignContent, alignSelf, and primaryTypographyProps={{ align: "center" }}, I am stumped.
What's the correct way to go about this?
Below is an example with center-aligned text.
import React from "react";
import { makeStyles, withStyles } from "#material-ui/core/styles";
import List from "#material-ui/core/List";
import ListItem from "#material-ui/core/ListItem";
import ListItemText from "#material-ui/core/ListItemText";
const useStyles = makeStyles(theme => ({
root: {
width: "100%",
maxWidth: 360,
backgroundColor: theme.palette.background.paper
}
}));
const CenteredListItemText = withStyles({
root: {
textAlign: "center"
}
})(ListItemText);
export default function SimpleList() {
const classes = useStyles();
return (
<div className={classes.root}>
<List component="nav" aria-label="main mailbox folders">
<ListItem button>
<CenteredListItemText primary="Inbox" secondary="Inbox secondary" />
</ListItem>
<ListItem button>
<CenteredListItemText primary="Drafts" secondary="Drafts secondary" />
</ListItem>
</List>
</div>
);
}

React-admin - Create sub menu

How to create sub menu in react-admin, because in admin-on-rest i can use prop menuItems in MenuItem Component.
Does react-admin have same props for this case?
I try create my own component, but with prop primary in ListItemText for give list name my app always show error Uncaught TypeError: Cannot read property '#global'
Thank you
As previously explained by the maintainers/developers of react-admin, this feature is not offered by react-admin.
If you want a sub-menu, you should create a custom menu, following the instructions given in the official react-admin documentation and implement the Material-UI's nested-list logic.
This is a simple example of what your sub-menu could look like, according to the links I provided:
<List component="nav" >
<ListItem button onClick={this.handleClick}>
<ListItemIcon>
<InboxIcon />
</ListItemIcon>
<ListItemText inset primary="YOUR-SECTION-TITLE" />
{this.state.open ? <ExpandLess /> : <ExpandMore />}
</ListItem>
<Collapse in={this.state.open} timeout="auto" unmountOnExit>
<List component="div" disablePadding>
<MenuItemLink to="/your-api-endpoint-1" primaryText="API-ENDPOINT-1" onClick={this.props.onMenuClick} />
<MenuItemLink to="/your-api-endpoint-2" primaryText="API-ENDPOINT-2" onClick={this.props.onMenuClick} />
<MenuItemLink to="/your-api-endpoint-3" primaryText="API-ENDPOINT-3" onClick={this.props.onMenuClick} />
</List>
</Collapse>
</List>
Simple submenu component compatible with React Admin v4 https://marmelab.com/react-admin/Menu.html
import * as React from 'react';
import { useState } from 'react';
import PropTypes from 'prop-types';
import ExpandMoreIcon from '#mui/icons-material/ExpandMore';
import { List, ListItem, ListItemText, Collapse } from '#mui/material';
import { useTranslate, useSidebarState } from 'react-admin';
export const SubMenu = (props: SubMenuProps) => {
const { isDropdownOpen = false, primaryText, leftIcon, children, ...rest } = props;
const translate = useTranslate();
const [open] = useSidebarState();
const [isOpen, setIsOpen] = useState(isDropdownOpen);
const handleToggle = () => {
setIsOpen(!isOpen);
};
return (
<React.Fragment>
<ListItem
dense
button
onClick={handleToggle}
sx={{
paddingLeft: '1rem',
color: 'rgba(0, 0, 0, 0.54)',
}}
>
{isOpen ? <ExpandMoreIcon /> : leftIcon}
<ListItemText
inset
disableTypography
primary={translate(primaryText)}
sx={{
paddingLeft: 2,
fontSize: '1rem',
color: 'rgba(0, 0, 0, 0.6)',
}}
/>
</ListItem>
<Collapse in={isOpen} timeout="auto" unmountOnExit>
<List
component="div"
disablePadding
sx={open ? {
paddingLeft: '25px',
transition: 'padding-left 195ms cubic-bezier(0.4, 0, 0.6, 1) 0ms',
} : {
paddingLeft: 0,
transition: 'padding-left 195ms cubic-bezier(0.4, 0, 0.6, 1) 0ms',
}}
>
{children}
</List>
</Collapse>
</React.Fragment>
)
}
export type SubMenuProps = {
children?: React.ReactNode;
isDropdownOpen?: boolean;
leftIcon?: React.ReactElement;
primaryText?: string;
};
export default SubMenu;
Usage:
import * as React from 'react';
import { Menu } from 'react-admin';
import BookIcon from '#mui/icons-material/Book';
import PeopleIcon from '#mui/icons-material/People';
import LabelIcon from '#mui/icons-material/Label';
import SubMenu from './SubMenu';
export const MainMenu = () => (
<Menu>
<Menu.DashboardItem />
<SubMenu primaryText="CMS" leftIcon={<BookIcon />}>
<Menu.Item to="/admin/pages" primaryText="Pages" leftIcon={<BookIcon />}/>
<Menu.Item to="/admin/articles" primaryText="Articles" leftIcon={<PeopleIcon />}/>
</SubMenu>
<Menu.Item to="/custom-route" primaryText="Miscellaneous" leftIcon={<LabelIcon />}/>
</Menu>
);

Multiline secondary text for list react component

I am trying to add a second line of the secondary text of a list react material ui component.
How can I modify it? See live demo here.
<ListItemText primary="Photos" secondary="first row" secondary="second row"/>
I edited on file demo.js of (demo link)
import React from "react";
import PropTypes from "prop-types";
import { withStyles } from "#material-ui/core/styles";
import List from "#material-ui/core/List";
import ListItem from "#material-ui/core/ListItem";
import ListItemText from "#material-ui/core/ListItemText";
import Avatar from "#material-ui/core/Avatar";
import ImageIcon from "#material-ui/icons/Image";
import WorkIcon from "#material-ui/icons/Work";
import BeachAccessIcon from "#material-ui/icons/BeachAccess";
const styles = theme => ({
root: {
width: "100%",
maxWidth: 360,
backgroundColor: theme.palette.background.paper
}
});
function FolderList(props) {
const { classes } = props;
return (
<div className={classes.root}>
<List>
<ListItem>
<Avatar>
<ImageIcon />
</Avatar>
<ListItemText
primary="Photos"
secondary={
<div>
<div>line 1</div>
<div>line 2</div>
</div>
}
/>
</ListItem>
<ListItem>
<Avatar>
<WorkIcon />
</Avatar>
<ListItemText primary="Work" secondary="Jan 7, 2014" />
</ListItem>
<ListItem>
<Avatar>
<BeachAccessIcon />
</Avatar>
<ListItemText primary="Vacation" secondary="July 20, 2014" />
</ListItem>
</List>
</div>
);
}
FolderList.propTypes = {
classes: PropTypes.object.isRequired
};
export default withStyles(styles)(FolderList);
secondary props is node type, You can pass props as:
secondary={
<div>
<div>line 1</div>
<div>line 2</div>
</div>
}

Resources