I'm using Material-UI and I'd like to use the List/ListItem Component to group my Radio Buttons.
Similar to this one:
<RadioButtonGroup ...>
<List>
<ListItem
...
nestedItems={[
<RadioButton ... />,
<RadioButton ... />
]}
/>
<ListItem
...
nestedItems={[
<RadioButton ... />,
<RadioButton ... />
]}
/>
</List>
</RadioButtonGroup>
Is there a way to archive this?
Thanks.
My temporary workaround is to use checkboxes which look and behave like radio buttons.
import List from 'material-ui/lib/lists/list';
import ListItem from 'material-ui/lib/lists/list-item';
import Checkbox from 'material-ui/lib/checkbox';
import RadioChecked from 'material-ui/lib/svg-icons/toggle/radio-button-checked';
import RadioUnchecked from 'material-ui/lib/svg-icons/toggle/radio-button-unchecked';
...
onChangeRadio = (event) => this.setState({ radioValue: event.target.value });
...
<List>
<ListItem
primaryText='First Group'
primaryTogglesNestedList={true}
nestedItems={[
<ListItem
primaryText='Radio 1'
leftCheckbox={
<Checkbox
value='1'
onCheck={this.onChangeRadio}
checked={this.state.radioValue == '1'}
checkedIcon={<RadioChecked />}
unCheckedIcon={<RadioUnchecked />}
/>
}
/>
<ListItem
primaryText='Radio 2'
leftCheckbox={
<Checkbox
value='2'
onCheck={this.onChangeRadio}
checked={this.state.radioValue == '2'}
checkedIcon={<RadioChecked />}
unCheckedIcon={<RadioUnchecked />}
/>}
/>
]}
/>
<ListItem
primaryText='Second Group'
primaryTogglesNestedList={true}
nestedItems={[
<ListItem
primaryText='Radio 3'
leftCheckbox={
<Checkbox
value='3'
onCheck={this.onChangeRadio}
checked={this.state.radioValue == '3'}
checkedIcon={<RadioChecked />}
unCheckedIcon={<RadioUnchecked />}
/>
}
/>
<ListItem
primaryText='Radio 4'
leftCheckbox={
<Checkbox
value='4'
onCheck={this.onChangeRadio}
checked={this.state.radioValue == '4'}
checkedIcon={<RadioChecked />}
unCheckedIcon={<RadioUnchecked />}
/>
}
/>
]}
/>
</List>
I hope there is a better solution than this.
Maybe this will help
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 ListItemSecondaryAction from '#material-ui/core/ListItemSecondaryAction';
import ListItemText from '#material-ui/core/ListItemText';
import Checkbox from '#material-ui/core/Checkbox';
import IconButton from '#material-ui/core/IconButton';
import CommentIcon from '#material-ui/icons/Comment';
import Radio from '#material-ui/core/Radio';
import RadioGroup from '#material-ui/core/RadioGroup';
import FormHelperText from '#material-ui/core/FormHelperText';
import FormControlLabel from '#material-ui/core/FormControlLabel';
import FormControl from '#material-ui/core/FormControl';
import FormLabel from '#material-ui/core/FormLabel';
const styles = theme => ({
root: {
width: '100%',
maxWidth: 360,
overflow: 'auto',
maxHeight: 500,
backgroundColor: theme.palette.background.paper,
},
});
class RadioList extends React.Component {
state = {
checked: 0,
};
handleToggle = value => () => {
this.setState({ checked: value });
};
render() {
const { classes } = this.props;
return (
<div className={classes.root}>
<List>
{[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10].map(value => (
<ListItem
key={value}
role={undefined}
dense
button
onClick={this.handleToggle(value)}
className={classes.listItem}
>
<FormControlLabel control={<Radio />}
checked={this.state.checked === value}
tabIndex={-1}
disableRipple
/>
<ListItemText primary={`Line item ${value + 1}`} />
</ListItem>
))}
</List>
</div>
);
}
}
RadioList.propTypes = {
classes: PropTypes.object.isRequired,
};
export default withStyles(styles)(RadioList);
Related
Here's my header copmponent where I try to apply active tabs effect, the text completely disappers.
Header.js
import {
AppBar,
Toolbar,
useScrollTrigger,
Typography,
Tabs,
Tab,
Button,
} from "#mui/material";
import DeveloperModeIcon from "#mui/icons-material/DeveloperMode";
import { Box } from "#mui/system";
import { styled } from "#mui/material/styles";
import { Link } from "react-router-dom";
const Header = () => {
const [value, setValue] = useState(window.location.pathname);
function ElevationScroll(props) {
const { children } = props;
const trigger = useScrollTrigger({
disableHysteresis: true,
threshold: 0,
});
return React.cloneElement(children, {
elevation: trigger ? 4 : 0,
});
}
const TabComponent = styled(Tab)(({ theme }) => ({
fontWeight: 600,
fontSize: "1rem",
textTransform: "none",
// color: theme.palette.common.white,
}));
const onTabChangeHandler = (event, newValue) => {
setValue(newValue);
};
return (
<React.Fragment>
<ElevationScroll>
<AppBar position="fixed">
<Toolbar>
<DeveloperModeIcon sx={{ fontSize: "3rem" }} />
<Typography variant="h4">Lopxhan Development</Typography>
<Tabs
sx={{ marginLeft: "auto" }}
value={value}
onChange={onTabChangeHandler}
aria-label="secondary tabs example"
>
<TabComponent label="Home" component={Link} to="/" value="/" />
<TabComponent
label="Services"
component={Link}
to="/services"
value="/services"
disableRipple
/>
<TabComponent
label="Projects"
component={Link}
to="/projects"
value="/projects"
/>
<TabComponent
label="About Us"
component={Link}
to="/aboutus"
value="/aboutus"
/>
<TabComponent
label="Conatct Us"
component={Link}
to="/contactus"
value="/contactus"
/>
</Tabs>
<Button
variant="contained"
color="secondary"
sx={{
borderRadius: "50px",
fontWeight: 600,
}}
>
Get a Quote
</Button>
</Toolbar>
</AppBar>
</ElevationScroll>
<Box
sx={[
(theme) => ({
marginBottom: {
...theme.mixins.toolbar,
},
}),
]}
/>
</React.Fragment>
);
};
export default Header;
Theme.js
const arcBlue = "#0B72B9";
const arcOrange = "#e67700";
const arcGrey = "#868686";
export const appTheme = createTheme({
palette: {
common: {
blue: arcBlue,
orange: arcOrange,
white: "#fff",
},
primary: {
main: arcBlue,
},
secondary: {
main: arcOrange,
},
},
});
App.js
import { Routes, Route } from "react-router-dom";
import { ThemeProvider } from "#mui/material/styles";
import Header from "./components/UI/Header";
import { appTheme } from "./components/UI/Theme";
import HomePage from "./components/pages/HomePage";
import ServicesPage from "./components/pages/ServicesPage";
import ProjectsPage from "./components/pages/ProjectsPage";
import GetAQuotePage from "./components/pages/GetAQuotePage";
import AboutUsPage from "./components/pages/AboutUsPage";
import ContactUsPage from "./components/pages/ContactUsPage";
function App() {
return (
<ThemeProvider theme={appTheme}>
<Header />
<Routes>
<Route path="/" element={<HomePage />} />
<Route path="/services" element={<ServicesPage />} />
<Route path="/projects" element={<ProjectsPage />} />
<Route path="/aboutus" element={<AboutUsPage />} />
<Route path="/contactus" element={<ContactUsPage />} />
<Route path="/getaquote" element={<GetAQuotePage />} />
</Routes>
</ThemeProvider>
);
}
export default App;
I tried applying textColor and indicatorColor to Tabs, and its kinda somehow shows the tab text but ripple effect changes to double click. Also ripple effect on tab changes to double click as soon as I add active tab effect.
Someone help me, I built an application with NEXT and Material UI and now for some reason even though the dev server works perfectly fine the productio build is breaking. I tried to alter my _document.ts as shown below:
import Document, { Html, Head, Main, NextScript } from 'next/document';
import React from 'react';
import { ServerStyleSheets } from '#mui/styles';
export default class MyDocument extends Document {
render() {
return (
<Html lang='en'>
<Head>
<link rel='preconnect' href='https://fonts.googleapis.com' />
<link rel='preconnect' href='https://fonts.gstatic.com' />
<link
href='https://fonts.googleapis.com/css2?family=Roboto:wght#100;300;400&display=swap'
rel='stylesheet'
/>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
// `getInitialProps` belongs to `_document` (instead of `_app`),
// it's compatible with server-side generation (SSG).
MyDocument.getInitialProps = async (ctx) => {
// Render app and page and get the context of the page with collected side effects.
const sheets = new ServerStyleSheets();
const originalRenderPage = ctx.renderPage;
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App) => (props) => sheets.collect(<App {...props} />),
});
const initialProps = await Document.getInitialProps(ctx);
return {
...initialProps,
// Styles fragment is rendered after the app and page rendering finish.
styles: [
...React.Children.toArray(initialProps.styles),
sheets.getStyleElement(),
],
};
};
and this is _app.tsx:
import '../styles/root.scss';
import '../styles/global/_page.scss';
import { Provider } from 'react-redux';
import type { AppProps } from 'next/app';
import Notification from '../components/Common/Notifications/Notification';
import store from '../redux/app/store';
import React from 'react';
import MIUIHeader from '../components/Common/MUIComponents/MUI-Header/MUIHead';
export default function MyApp({ Component, pageProps }: AppProps) {
React.useEffect(() => {
// Remove the server-side injected CSS.
const jssStyles = document.querySelector('#jss-server-side');
if (jssStyles) {
//#ts-ignore
jssStyles.parentElement.removeChild(jssStyles);
}
}, []);
return (
<Provider store={store}>
<React.Fragment>
<MIUIHeader />
<Component {...pageProps} />
<Notification />
</React.Fragment>
</Provider>
);
}
And here's my Material UI Component:
import React, { ReactElement } from 'react';
import TextField from '#mui/material/TextField';
import LocalPhoneIcon from '#mui/icons-material/LocalPhone';
import MuiPhoneNumber from 'material-ui-phone-number';
import Autocomplete from '#mui/material/Autocomplete';
import Button from '#mui/material/Button';
import MailIcon from '#mui/icons-material/Mail';
import PersonIcon from '#mui/icons-material/Person';
import LocationOnIcon from '#mui/icons-material/LocationOn';
import LocationCityIcon from '#mui/icons-material/LocationCity';
import BusinessCenterIcon from '#mui/icons-material/BusinessCenter';
import ShareIcon from '#mui/icons-material/Share';
import classes from './VibeForms.module.scss';
import CorporateFareIcon from '#mui/icons-material/CorporateFare';
import ImageCarasoul from '../../../ImageCarasoul/ImageCarasoul';
interface Props {}
const imagePaths = [
'/screen-graphics/signup/individual/graphic-1.svg',
'/screen-graphics/signup/individual/graphic-2.svg',
'/screen-graphics/signup/individual/graphic-3.svg',
];
const maxMultilineRows = 3;
export default function LoginForm({}: Props): ReactElement {
return (
<React.Fragment>
<div className={classes.LoginColumnGrid}>
<ImageCarasoul imagePaths={imagePaths} />
<div className={classes.LoginColumn}>
<div className={classes.LoginRowGrid}>
<PersonIcon />
<TextField
required
className={classes.FormInputField}
label='Full Name'
size='small'
type='text'
/>
</div>
<div className={classes.LoginRowGrid}>
<MailIcon />
<TextField
required
className={classes.FormInputField}
label='Email Address'
size='small'
type='email'
/>
</div>
<div className={classes.LoginRowGrid}>
<LocalPhoneIcon />
<MuiPhoneNumber
variant='outlined'
name='phone'
data-cy='user-phone'
required
className={classes.FormInputField}
label='Phone Number'
size='small'
defaultCountry={'in'}
onChange={(e) => console.log(e)}
/>
</div>
<div className={classes.LoginRowGrid}>
<ShareIcon />
<TextField
required
className={classes.FormInputField}
label='Social Media Links'
size='small'
type='text'
multiline
maxRows={maxMultilineRows}
/>
</div>
<div className={classes.LoginRowGrid}>
<CorporateFareIcon />
<Autocomplete
disablePortal
options={[
'Workshop/Training',
'Social Media Promotion',
'Our Tie Ups',
'Networking',
]}
renderInput={(params) => (
<TextField
{...params}
label='Services you are looking for'
required
className={classes.FormInputField}
size='small'
/>
)}
/>
</div>
<div className={classes.LoginRowGrid}>
<CorporateFareIcon />
<TextField
required
className={classes.FormInputField}
label='Any Other Specific Requests?'
size='small'
type='text'
multiline
maxRows={maxMultilineRows}
/>
</div>
</div>
<div className={classes.LoginColumn}>
<div className={classes.LoginRowGrid}>
<CorporateFareIcon />
<Autocomplete
disablePortal
options={['Beginer', 'Intermediate', 'Expert']}
renderInput={(params) => (
<TextField
{...params}
label='Enter your Profession'
required
className={classes.FormInputField}
size='small'
/>
)}
/>
</div>
<div className={classes.LoginRowGrid}>
<LocationOnIcon />
<Autocomplete
disablePortal
options={['Bangalore', 'Chennai', 'Hyderabad', 'Mumbai']}
renderInput={(params) => (
<TextField
{...params}
label='State'
required
className={classes.FormInputField}
size='small'
/>
)}
/>
</div>
<div className={classes.LoginRowGrid}>
<LocationCityIcon />
<Autocomplete
disablePortal
options={['Bangalore', 'Chennai', 'Hyderabad']}
renderInput={(params) => (
<TextField
{...params}
label='City'
required
className={classes.FormInputField}
size='small'
/>
)}
/>
</div>
<div className={classes.LoginRowGrid}>
<CorporateFareIcon />
<Autocomplete
disablePortal
options={['Beginer', 'Intermediate', 'Expert']}
renderInput={(params) => (
<TextField
{...params}
label='Profession Level'
required
className={classes.FormInputField}
size='small'
/>
)}
/>
</div>
<div className={classes.LoginRowGrid}>
<BusinessCenterIcon />
<TextField
required
className={classes.FormInputField}
label='Work Experience'
size='small'
type={'number'}
InputProps={{
endAdornment: <label>Years</label>,
}}
/>
</div>
<div className={classes.LoginRowGrid}>
<CorporateFareIcon />
<TextField
required
className={classes.FormInputField}
label='Your Achievements'
size='small'
type='text'
multiline
maxRows={maxMultilineRows}
/>
</div>
</div>
</div>
<Button
variant='contained'
style={{ alignSelf: 'end', margin: '1rem', width: '10rem' }}>
Next
</Button>
</React.Fragment>
);
}
I am using sass modules along with Material UI in NEXT. I tried all the solutions out there mentioned in github and stack overflow but nothing seems to work ;(.
Since most of the documents were outdated to an older version of Material UI, I found the solution in their github repo. They have updated it 20 days back and it works fine. In case someone may want to reference this in future, kindly keep checking this repo.
Here you will find the exact configuration you need to do for _app.tsx and _document.tsx. Additionally you need to also create two files for themes and createEmotionCache as well. The location doesn't matter though.
How can I control the shape of the extra item that is appended to a React Material-UI AvatarGroup (when the number of Avatars is more than max) so that it matches the rounded variant of the Avatars.
<AvatarGroup max={4}>
<Avatar
variant="rounded"
alt="Remy Sharp"
src="/static/images/avatar/1.jpg"
/>
<Avatar
variant="rounded"
alt="Travis Howard"
src="/static/images/avatar/2.jpg"
/>
<Avatar
variant="rounded"
alt="Cindy Baker"
src="/static/images/avatar/3.jpg"
/>
<Avatar
variant="rounded"
alt="Agnes Walker"
src="/static/images/avatar/4.jpg"
/>
<Avatar
variant="rounded"
alt="Trevor Henderson"
src="/static/images/avatar/5.jpg"
/>
</AvatarGroup>
API doc doesn't mention how to set this: https://material-ui.com/api/avatar-group/
Although the "next" version does: https://next.material-ui.com/es/api/avatar-group/
You can override the shape's style using a mui global class selectors:
import React from "react";
import Avatar from "#material-ui/core/Avatar";
import AvatarGroup from "#material-ui/lab/AvatarGroup";
import { createStyles, makeStyles, Theme } from "#material-ui/core/styles";
const useStyles = makeStyles((theme: Theme) =>
createStyles({
root: {
"& .MuiAvatar-root": { borderRadius: 0 }
}
})
);
export default function GroupAvatars() {
const classes = useStyles();
return (
<AvatarGroup max={4} className={classes.root}>
<Avatar
alt="Remy Sharp"
variant="rounded"
src="/static/images/avatar/1.jpg"
/>
<Avatar
alt="Travis Howard"
variant="rounded"
src="/static/images/avatar/2.jpg"
/>
<Avatar
alt="Cindy Baker"
variant="rounded"
src="/static/images/avatar/3.jpg"
/>
<Avatar
alt="Agnes Walker"
variant="rounded"
src="/static/images/avatar/4.jpg"
/>
<Avatar
alt="Trevor Henderson"
variant="rounded"
src="/static/images/avatar/5.jpg"
/>
</AvatarGroup>
);
}
please find a working example on codesandbox
I have this code:
import React from 'react';
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 ArrowForwardIos from '#material-ui/icons/ArrowForwardIos';
import ListSubheader from '#material-ui/core/ListSubheader';
import Switch from '#material-ui/core/Switch';
import TextField from '#material-ui/core/TextField';
import ListItemAvatar from '#material-ui/core/ListItemAvatar';
import Avatar from '#material-ui/core/Avatar';
import FolderIcon from '#material-ui/icons/Folder';
import ListItemSecondaryAction from '#material-ui/core/ListItemSecondaryAction';
import DeleteIcon from '#material-ui/icons/Delete';
import IconButton from '#material-ui/core/IconButton';
function App() {
return (
<ListItem>
<ListItemAvatar>
<Avatar>
<FolderIcon />
</Avatar>
</ListItemAvatar>
<ListItemText
primary="Single-line item"
secondary="Secondary text"
/>
<ListItemSecondaryAction>
<IconButton edge="end" aria-label="delete">
<DeleteIcon />
</IconButton>
</ListItemSecondaryAction>
</ListItem>
);
}
export default App;
"Single-line item" and "Secondary text" is vertically aligned, but I need align it horizontally. Do you have any idea how to do it?
Instead of left I need rendering on the right here:
https://gitlab.com/j4nos/tableitem/blob/master/src/App.js
How? :)
I managed to get the desired output using a few other elements and referring the material-ui documentation. here is a working example: https://codesandbox.io/s/sweet-bose-4e26h
function App() {
return (
<ListItem>
<ListItemAvatar>
<Avatar>
<FolderIcon />
</Avatar>
</ListItemAvatar>
<Box
textAlign="right"
style={{ paddingRight: 5 }}
>
Single-line item
</Box>
<ListItemText
secondaryTypographyProps={{ align: "left" }}
secondary="Secondary text"
/>
<ListItemSecondaryAction>
<IconButton edge="end" aria-label="delete">
<DeleteIcon />
</IconButton>
</ListItemSecondaryAction>
</ListItem>
);
}
Alternative using Material-UI's built-in overrides on ListItemText.classes.root as seen below:
<ListItem className={classes.listItem}>
<ListItemText
classes={{ root: classes.listItemRootOverride }}
className={classes.listText}
primary={`${tc('Foo')}:`}
/>
<ListItemText
classes={{ root: classes.listItemRootOverride }}
secondary={bars}
/>
</ListItem>
Classes in useStyles:
listItemRootOverride: { /* this is the important one */
flex: 'none',
paddingRight: 4
},
listItem: {
padding: theme.spacing(0)
},
listText: {
color: theme.palette.common.black,
paddingRight: theme.spacing(2)
}
Example output (disregard minor alignment issue):
Material ui add default padding on List and ListItem how to remove it ?
Any help or direction to resources would be appreciated.
You can override the root class of the ListItem component and pass the padding you want.
const styles = theme => ({
root: {
width: "100%",
maxWidth: 360,
backgroundColor: theme.palette.background.paper
},
item: {
padding: 0
}
});
function SimpleList(props) {
const { classes } = props;
return (
<div className={classes.root}>
<List component="nav">
<ListItem button classes={{ root: classes.item }}>
<ListItemText primary="Item 01" />
</ListItem>
<ListItem button classes={{ root: classes.item }}>
<ListItemText primary="Item 02" />
</ListItem>
<ListItem button classes={{ root: classes.item }}>
<ListItemText primary="Item 03" />
</ListItem>
</List>
</div>
);
}
See working sample.
For anyone else coming here the easy way to remove the default padding from the list is to use the disablePadding prop on the List Component.
https://material-ui.com/api/list/
import React from 'react'
import List from '#material-ui/core/List'
import ListItem from '#material-ui/core/ListItem'
import ListItemText from '#material-ui/core/ListItemText'
function Sidebar() {
return (
<List disablePadding>
<ListItem button>
<ListItemText>Home</ListItemText>
</ListItem>
<ListItem button>
<ListItemText>Billing</ListItemText>
</ListItem>
<ListItem button>
<ListItemText>Settings</ListItemText>
</ListItem>
</List>
)
}
export default Sidebar
If you're just using a ListItem component, you can use the disableGutters (Boolean) property to disable the left and right padding, as seen from the API documentation: https://material-ui.com/api/list-item/
<ListItem disableGutters={true} button={true} key='Home' component={Link} to={"/"} selected={'/' === pathname}>
<ListItemIcon><Home /></ListItemIcon>
<ListItemText primary='Home' />
</ListItem>