How to customize Material UI autocomplete dropdown menu - javascript

I am using Material UI v5 beta1 and I have been trying to customize their Autocomplete component so that the Typography color on the options changes from black to white whenever the item is selected, however, I can't figure out how to pass the style to it.
So far I have tried passing my styles on .MuiAutocomplete-option either through a styled component or a global override (see code attached) and tried every state I can think of, hover, selected, focused, even tried the Material classes for those but none of them worked. I have also tried using a custom Popper with a MenuList inside but with no luck. I have been pulling my hair with this for a couple of days now, and not being able to inspect the DOM makes it even harder, any help or tip would be highly appreciated.
Thank you in advance.
MuiAutocomplete: {
styleOverrides: {
root: {
// ...
},
option: {
padding: '5px 12px',
backgroundColor: theme.palette.background.paper,
display: 'flex',
height: 42,
borderRadius: theme.borderRadius.s,
'&:hover': {
backgroundColor: theme.palette.action.hover,
'& .Mui-Typography-root': { color: theme.palette.text.contrast },
},
'& .Mui-selected': {
backgroundColor: theme.palette.action.hover,
'& .Mui-Typography-root': { color: theme.palette.text.contrast },
},
'&.disabled': {
opacity: 0.5,
'& .Mui-Typography-root': {
color: theme.palette.text.disabled,
},
},
},
renderOption={(props, option) => {
return (
<li {...props}>
<Box display={'flex'} flexDirection={'row'}>
<Avatar size={'32'} variant={'circular'} />
<Box display={'flex'} ml={3} flexDirection={'column'}>
<Typography color={'text.primary'}>
{option.label}
</Typography>
<Typography color={'text.secondary'}>
Extra Information
</Typography>
</Box>
</Box>
</li>
);
}}

I would pass in { selected } to your renderOption, then use it to toggle your styling inline
For example:
renderOption={(props, option, { selected }) => {
return (
<li {...props}>
<Box display={'flex'} flexDirection={'row'} style={{ backgroundColor: selected ? 'red' : 'green' }}>
<Avatar size={'32'} variant={'circular'} />
<Box display={'flex'} ml={3} flexDirection={'column'}>
<Typography color={'text.primary'}>
{option.label}
</Typography>
<Typography color={'text.secondary'}>
Extra Information
</Typography>
</Box>
</Box>
</li>
);
}}

Related

Material UI with React: How to style <Autocomplete/> with rounded corners

I've always been a fan of Google's search bar, with nice rounded corners and ample padding around the text.
I'm trying to replicate this style using Material UI's <Autocomplete/> component, but I can't seem to do it. I'm using Next.js. What I have so far is:
import React, { useState, useEffect } from 'react';
import TextField from '#mui/material/TextField';
import Stack from '#mui/material/Stack';
import Autocomplete from '#mui/material/Autocomplete';
import { borderRadius, Box } from '#mui/system';
import SearchIcon from '#material-ui/icons/Search';
const LiveSearch = (props) => {
const [jsonResults, setJsonResults] = useState([]);
useEffect(() => {
fetch(`https://jsonplaceholder.typicode.com/users`)
.then(res => res.json())
.then(json => setJsonResults(json));
}, []);
return (
<Stack sx={{ width: 400, margin: "auto"}}>
<Autocomplete
id="Hello"
getOptionLabel={(jsonResults) => jsonResults.name}
options={jsonResults}
noOptionsText="No results"
isOptionEqualToValue={(option, value) => {
option.name === value.name
}}
renderOption={(props, jsonResults) => (
<Box component="li" {...props} key={jsonResults.id}>
{jsonResults.name} - Ahhh
</Box>
)}
renderInput={(params) => <TextField {...params} label="Search users..." />}
/>
</Stack>
)
}
export default LiveSearch;
The above code should run as-is – there's an axios call in there to populate the autocomplete results too.
I've tried various way to get the <SearchIcon /> icon prefix inside the input with no success, but really I'd just be happy if I could figure out how to pad it. You can see in the google screenshot how the autocomplete lines up really well with the box, but in my version, the border-radius just rounds the element, and so it no longer lines up with the dropdown.
I'm new to Material UI, so I'm still not quite sure how to do these styles, but I think the issue is that the border is being drawn by some internal element, and although I can set the borderRadius on the component itself global CSS:
.MuiOutlinedInput-root {
border-radius: 30px;
}
I can't seem to set the padding or borders anywhere. I've also tried setting style with sx but it does nothing.
You have to look at the autocomplete css classes and override them in your component or use them in your theme, if you use one.
<Autocomplete
componentsProps={{
paper: {
sx: {
width: 350,
margin: "auto"
}
}
}}
id="Hello"
notched
getOptionLabel={(jsonResults) => jsonResults.name}
options={jsonResults}
noOptionsText="No results"
isOptionEqualToValue={(option, value) => {
option.name === value.name;
}}
renderOption={(props, jsonResults) => (
<Box component="li" {...props} key={jsonResults.id}>
{jsonResults.name} - Ahhh
</Box>
)}
renderInput={(params) => (
<TextField
{...params}
label="Search users..."
sx={{
"& .MuiOutlinedInput-root": {
borderRadius: "50px",
legend: {
marginLeft: "30px"
}
},
"& .MuiAutocomplete-inputRoot": {
paddingLeft: "20px !important",
borderRadius: "50px"
},
"& .MuiInputLabel-outlined": {
paddingLeft: "20px"
},
"& .MuiInputLabel-shrink": {
marginLeft: "20px",
paddingLeft: "10px",
paddingRight: 0,
background: "white"
}
}}
/>
)}
/>
Sandbox: https://codesandbox.io/s/infallible-field-qsstrs?file=/src/Search.js
I'm trying to figure out how to line up the edges (figured it out, see update), but this is how I was able to insert the Search icon, via renderInput and I got rid of the expand and collapse arrows at the end of the bar by setting freeSolo={true} (but this allows user input to not be bound to provided options).
import { Search } from '#mui/icons-material';
import { Autocomplete, AutocompleteRenderInputParams, InputAdornment } from '#mui/material';
...
<Autocomplete
freeSolo={true}
renderInput={(renderInputParams: AutocompleteRenderInputParams) => (
<div ref={renderInputParams.InputProps.ref}
style={{
alignItems: 'center',
width: '100%',
display: 'flex',
flexDirection: 'row'
}}>
<TextField style={{ flex: 1 }} InputProps={{
...renderInputParams.InputProps, startAdornment: (<InputAdornment position='start'> <Search /> </InputAdornment>),
}}
placeholder='Search'
inputProps={{
...renderInputParams.inputProps
}}
InputLabelProps={{ style: { display: 'none' } }}
/>
</div >
)}
...
/>
Ignore the colors and other styling, but this is what it looks like:
Update
I was able to line up the edges by controlling the border-radius via css and setting the bottom left and right to 0 and top ones to 20px.
Here's a demo:
Here are the changes I had to make in css. I also left the bottom border so there is a division between the search and the results, but you can style if however you like. (Also I'm using scss so I declared colors as variables at the top).
div.MuiAutocomplete-root div.MuiOutlinedInput-root { /* Search bar when not in focus */
border-radius: 40px;
background-color: $dark-color;
}
div.MuiAutocomplete-root div.MuiOutlinedInput-root.Mui-focused { /* Search bar when focused */
border-radius: 20px 20px 0px 0px !important;
}
div.MuiAutocomplete-root div.Mui-focused fieldset { /* fieldset element is what controls the border color. Leaving only the bottom border when dropdown is visible */
border-width: 1px !important;
border-color: transparent transparent $light-gray-color transparent !important;
}
.MuiAutocomplete-listbox { /* To control the background color of the listbox, which is the dropdown */
background-color: $dark-color;
}
div.MuiAutocomplete-popper div { /* To get rid of the rounding applied by Mui-paper on the dropdown */
border-top-right-radius: 0px;
border-top-left-radius: 0px;
}
You simply add border radius to fieldset:
<Autocomplete
sx={{ '& fieldset': { borderRadius: 33 }}}
/>
Codesandbox

I want to show and hide a form on toggle of a radio button in React js . Im trying how to use react hooks to hide or show a component on onChange even

Now i have used state hook to hide the Form when the page loads. And upon the click or toggle of the radio I'm able to show the Form, But if i toggle the radio again, the form is not hiding.
This is what i have implemented:
const WelcomeTab = () => {
const [toggle, settoggle] = useState(false);
return (
<React.Fragment>
<Tab.Pane
style={{
borderRadius: '7px',
padding: '30px',
}}
attached={false}
>
<Grid>
<Grid.Row>
<Grid.Column floated="left" width={8}>
<Header
style={{
fontSize: '18px',
fontFamily: 'Nunito-Regular',
color: '#4F4F4F',
}}
>
Welcome Screen
</Header>
</Grid.Column>
<Grid.Column floated="right" width={4}>
<Header
as="h4"
style={{
display: 'flex',
justifyContent: 'space-around',
marginLeft: '30px',
}}
>
Customize
<Radio toggle onChange={() => settoggle({ toggle: !toggle })} />
</Header>
</Grid.Column>
</Grid.Row>
</Grid>
{toggle ? (
<Form style={{ paddingTop: '20px' }}>
<Form.Field>
<label style={lableStyle}>Title</label>
<input style={{ marginBottom: '20px' }} />
<label style={lableStyle}>Message</label>
<TextArea />
</Form.Field>
</Form>
) : null}
</Tab.Pane>
</React.Fragment>
);
};
const lableStyle = {
fontFamily: 'Nunito-Regular',
fontWeight: 400,
color: '#4F4F4F',
fontSize: '15px',
display: 'inline-block',
marginBottom: '10px',
};
export default WelcomeTab;
try to add useEffect hook along with change like below,
you no longer need to {} this is old syntax of setState, using hooks we directly make the changes, hope this helps
useEffect(()=>{},[toggle])
replace this wrong syntax code, i can see its not json its boolean value
<Radio toggle onChange={()=>settoggle({toggle: !toggle})}/>
as this is old syntax not work with hooks, try to implment this instead,
<Radio toggle onChange={()=>settoggle(!toggle)}/>

Material-UI dark opacity background image

I'm wrapping all the widgets in a CardMedia component, and setting an image.
return (
<CardMedia image={bg} className={classes.bg}>
<main className={classes.content}>
<div className={classes.toolbar} />
<Grid container justify="center" spacing={4}>
{products.map((product) => (
<Grid item key={product.id} xs={12} sm={12} md={12} lg={6}>
<Product product={product}></Product>
</Grid>
))}
</Grid>
</main>
</CardMedia>);
I need to make the image a little darker.
Here is the code for the styles:
export default makeStyles((theme) => ({
toolbar: theme.mixins.toolbar,
content: {
flexGrow: 1,
backgroundColor: "transparent",
/* backgroundColor: theme.palette.background.default, */
padding: theme.spacing(3),
[theme.breakpoints.down("lg")]: {
paddingRight: 200,
paddingLeft: 200,
},
[theme.breakpoints.down("xl")]: {
paddingRight: 200,
paddingLeft: 200,
},
[theme.breakpoints.down("sm")]: {
padding: theme.spacing(3),
},
},
root: {
flexGrow: 1,
},
bg: {
backgroundRepeat: "no-repeat",
backgroundAttachment: "fixed",
},
}));
Maybe there is even a simpler way to create a background image with a darker tint?
Also, I don't know if this has to do with anything, but when I set the image with CardMedia, every time I scroll to the bottom of the page there is a short annoying lag. Thanks in advance.
Hi you should try to use this for made your image darker is a simple CSS function :
.img { filter: “brightness(50%)”; }
Also change your component if you not rendering any childrens inside :
<Product><\Product>
to
<Product />

change the font size of the checkbox and radio buttons labels

I am trying to change the font size of the checkbox and radio buttons labels.
I tried giving css to this component Checkbox but it's not changing the label's font size
since the labels are added dynamically.
so I researched and found this links. https://material-ui.com/api/radio/#css https://material-ui.com/customization/components/#overriding-styles-with-classes
but this is not helping me.
can you tell me how to fix it.
providing my code snippet and sandbox below.
https://codesandbox.io/s/material-demo-u95z5
const styles = theme => ({
root: {
display: "flex"
},
formControl: {
margin: theme.spacing.unit * 3
},
group: {
margin: `${theme.spacing.unit}px 0`
},
checkboxCSS: {
border: "1px solid red",
fontSize: 40
}
});
return (
<div className={classes.root}>
<FormControl component="fieldset" className={classes.formControl}>
<FormLabel component="legend">Gender</FormLabel>
<RadioGroup
aria-label="Gender"
name="gender1"
className={classes.group}
value={this.state.value}
onChange={this.handleRadioValueChange}
>
{radioValues.map(radio => {
return (
<FormControlLabel
value={radio.value}
control={<Radio />}
label={radio.label}
/>
);
})}
</RadioGroup>
{checkBoxvalues.map((check, index) => {
console.log("this.state[check.value]", this.state[check.value]);
return (
<FormControlLabel
key={check.value}
control={
<Checkbox
checked={check.checked}
onChange={this.handleCheckBoxChange(check.value, index)}
value={check.value}
className={classes.checkboxCSS}
/>
}
label={check.label}
/>
);
})}
</FormControl>
</div>
);
This link helped me: https://material-ui.com/api/form-control-label/#css
The following works in your sandbox.
In the classes object:
{
...,
checkboxLabel: {
fontSize: 40
}
}
On the FormControlLabel component add:
classes={{
label:classes.checkboxLabel
}}

How to make checkbox "Badge" in MaterialUI?

According to the API of Badge https://material-ui.com/api/badge/ there is a prop component which takes either a string to use a DOM element or a component.
In My code
<Badge color="primary" classes={{ badge: classes.badge }} component="checkbox">
<Avatar className={classes.orangeAvatar}>AP</Avatar>
</Badge>
OR
import Checkbox from '#material-ui/core/Checkbox';
<Badge color="primary" classes={{ badge: classes.badge }} component={Checkbox}>
<Avatar className={classes.orangeAvatar}>AP</Avatar>
</Badge>
In both the cases I am no getting checkbox as badge. How to do this?
Use Checkbox component. You can use component for icon props when it is not checked and checkedIcon props when it is checked.
<Checkbox
icon={<Avatar className={classes.purpleAvatar}> AP</Avatar>}
checkedIcon={<Avatar className={classes.orangeAvatar}> AP</Avatar>}
/>
I was stuck on the same problem and worked out as below.
Use badgeContent property.
badgeContent property accepts node and shows it as a badge content.
import CheckIcon from '#material-ui/icons/Check'
<Badge
color="secondary"
anchorOrigin={{
vertical: 'top',
horizontal: 'right',
}}
badgeContent={<CheckIcon style={{ fontSize: 10, padding: 0, color: 'white' }} />}
classes={{ badge: classes.badge }}
>
<Avatar className={classes.orangeAvatar}>AP</Avatar>
</Badge>
After that you can customize that badge content style like this.
import { Badge } from "#material-ui/core";
import { makeStyles } from "#material-ui/core/styles";
const useStyles = makeStyles((theme) => ({
badge: {
fontSize: 30
}
}));
export default function App() {
const classes = useStyles();
return (
<div className="App">
<Badge
badgeContent={"h"}
color="secondary"
classes={{ badge: classes.badge }}
/>
</div>
);
}
In my case I used following style to change badge size.
badge: {
fontSize: 16,
padding: 0,
minWidth: '12px !important',
height: '12px !important',
},
More discussion about customization will be found How to change font size of material ui badge content in reactjs?.

Categories