Material-UI: Full width List inside of Grid - javascript

Using material-ui I am trying to make a <List /> inside of a <Grid /> column. I want the list to scroll on overflow but also take up the width of the grid it is in. I can get both of these to work but not at the same time. the first section with the <Grid /> is the overflowY functioning properly but without the full width of the <ListItem />:
const styles = theme => ({
root: {
flexGrow: 1,
},
list: {
flexGrow: 1,
position: 'absolute',
overflow: 'auto',
bottom: '1em',
top: '1em',
},
})
...
<Grid item xs>
<List classes={{ root: classes.list }}>
{genereateData(35).map(item => (
<ListItem button>
<ListItemText primary={item} />
</ListItem>
))}
</List>
</Grid>
...
<List classes={{ root: classes.root }}>
but if I switch the line to the last little bit of code above the full width part works. So why would the flexGrow: 1 work in the root prop but not the list prop, and is there a way to get both? I assume it has to do with position: absolute but I cannot get rid of that. Just looking for a way to get both.
Here is a codesandbox!

Related

How to identify the bottom when scroller reaches to end of div in Reactjs

Below Is the div in its has a scroller with overflow. But what I want when I reach to the bottom of that div using a scroller. I want to load more items. I will append in resultList but don't know how to identify the bottom of div.
const ListItemScroll = (props) => {
let { resultList, indexChange, radioValue, radioHandleChange } = props
return (
<div style={{ overflow: "auto", overflowX: "hidden", height: "90%" }}>
<FormControl component="fieldset" variant="standard">
{
resultList[indexChange].dataList.map((list) => {
return (
<RadioGroup aria-label="gender"
name="controlled-radio-buttons-group"
value={radioValue[indexChange].value}
onChange={(e) => radioHandleChange(e, list)}>
<FormControlLabel size="small" value={list.name} control={<Radio size='small' />} label={list.name} />
</RadioGroup>
)
})
}
</FormControl>
</div>
)
}
You need to implement Intersection Observer to identify if the div is in viewport or not. Insert an empty div before </FormControl> and use Intersection Observer on that div.
See here: https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API

Displaying/hiding JSON info based on clicked item in React/MaterialUI

I'm creating an FAQ section based on a JSON file I have that has an array of objects with 'Question' and 'Answer.
It's set up to where when you click on the question on the left, it displays the corresponding answer to the right...which does work correctly. The problem is I want to hide the data when the user clicks on that question again and that part is not working. When I click on a question again it will change the icon but doesn't change the box on the right. I want the Answer box to completely disappear when the question is clicked on again.
I'm guessing it's something that I'm doing wrong with setting the state:
const [clickedIndex, setClickedIndex] = useState({});
const [displayAnswer, setDisplayAnswer] = useState();
const handleClick = (index) => () => {
setClickedIndex((state) => ({
[index]: !state[index],
}));
setDisplayAnswer(faqdata[index].Answer);
console.log(displayAnswer);
};
<Grid container spacing={3}>
<Grid item>
<List
style={{
maxHeight: 430,
width: 500,
overflow: 'auto',
border: '1px solid black',
}}
>
{faqdata.map((item, index) => (
<ListItem style={{ cursor: 'pointer' }}>
<ListItemIcon>
{clickedIndex[index] ? <RemoveIcon /> : <AddIcon />}
</ListItemIcon>
<ListItemText
primary={item.Question}
onClick={handleClick(index)}
/>
</ListItem>
))}
</List>
</Grid>
<Grid item>
<List
style={{
maxHeight: 430,
width: 500,
overflow: 'auto',
border: '1px solid black',
}}
>
<ListItem>{displayAnswer}</ListItem>
</List>
</Grid>
</Grid>
I would consider using a single state variable to store the currently open answer which can be:
null (meaning that no answer is open)
a number (meaning the answer to the question with that index is open)
const Container = () => {
const [openQuestion, setOpenQuestion] = useState(null);
const toggleOpenQuestion = (index) => {
if(openQuestion === index) {
setOpenQuestion(null)
}
setOpenQuestion(index)
}
return <UIComponents openQuestion={openQuestion} toggleOpenQuestion={toggleOpenQuestion}/>
}
Then in the UI you can work out what to display based on this value as follows:
<Grid container spacing={3}>
<Grid item>
<List
style={{
maxHeight: 430,
width: 500,
overflow: "auto",
border: "1px solid black",
}}
>
{faqdata.map((item, index) => (
<ListItem style={{ cursor: "pointer" }}>
<ListItemIcon>
{openQuestion === index ? <RemoveIcon /> : <AddIcon />}
</ListItemIcon>
<ListItemText primary={item.Question} onClick={() => toggleOpenQuestion(index)} />
</ListItem>
))}
</List>
</Grid>
<Grid item>
<List
style={{
maxHeight: 430,
width: 500,
overflow: "auto",
border: "1px solid black",
}}
>
<ListItem>{displayAnswer}</ListItem>
</List>
</Grid>
</Grid>;
Worth mentioning here that you should probably wrap the toggleOpenQuestion function in a useCallback to prevent uneccessary re-renders in the child component:
const toggleOpenQuestion = useCallback((index) => {
if(openQuestion === index) {
setOpenQuestion(null)
}
setOpenQuestion(index)
}, [openQuestion])

Adding a linear gradient to Material UI icon

I've tried the process described in this question Trying to add linear gradient to a martialUI icon as a comment stated it should work, but it does not for me.
Thinking that maybe the icons counted as text, I've tried several of the ways to add gradients to text such as here: https://css-tricks.com/snippets/css/gradient-text/.
Yet I haven't gotten anything to work. The gradient either shows as a box image in the foreground, in front of the icon, or just not at all. Does anyone know how to add a gradient to Material UI icons?
EDIT: Forgot to post my code, here's the relevant snippet:
const useStyles = makeStyles((theme) => ({
root: {
display: 'flex',
},
appBar: {
zIndex: theme.zIndex.drawer + 1,
},
drawer: {
width: drawerWidth,
flexShrink: 0
},
drawerPaper: {
width: drawerWidth,
backgroundColor:muiTheme.palette.secondary.main
},
drawerContainer: {
overflow: 'auto',
background:muiTheme.palette.secondary.main
},
content: {
flexGrow: 1,
padding: theme.spacing(3),
},
sideBarButton: {
fill: "#FFFFFF",
fontSize: "50px"
}
}));
const SideBar = (props) => {
const classes = useStyles();
return (
<MuiThemeProvider theme={muiTheme}>
<Drawer
className={classes.drawer}
variant="permanent"
classes={{
paper: classes.drawerPaper,
}}
>
<Toolbar />
<div className={classes.drawerContainer}>
<Box display="flex" flexDirection="column" padding="0" margin="0" >
<List>
<ListItem button>
<ListItemIcon> <SearchIcon className={classes.sideBarButton}/> </ListItemIcon>
<ListItemText/>
</ListItem>
<ListItem button>
<ListItemIcon> <AddCircleIcon className={classes.sideBarButton}/> </ListItemIcon>
<ListItemText/>
</ListItem>
</List>
</Box>
</div>
</Drawer>
</MuiThemeProvider>
)
}
More precisely, this is the list item (which is a material icon) I'm trying to add gradient to:
<ListItemIcon>
<SearchIcon className{classes.sideBarButton}/>
</ListItemIcon>
And this is the style applied:
sideBarButton: {
background: "linear-gradient(to right, #ad5389, #3c1053)",
fontSize: "50px"
}
The other methods I've tried per the links above:
// Just put the style in the tag, doesn't compile
<SearchIcon className={classes.sideBarButton} style={{linear-gradient(to right bottom, #FD297B, #FF5864, #FF655B)}}/>
Another method:
sideBarButton:{
background: "-webkit-linear-gradient(#eee, #333)",
WebkitBackgroundClip: "text",
WebkitTextFillColor: "transparent",
fontSize: "50px"
}
Yet another method via https://fossheim.io/writing/posts/css-text-gradient/ :
sideBarButton:{
/* Create the gradient. */
backgroundImage: "linear-gradient(45deg, #f3ec78, #af4261)",
/* Set the background size and repeat properties. */
backgroundSize: "100%",
backgroundRepeat: "repeat",
/* Use the text as a mask for the background. */
/* This will show the gradient as a text color rather than element bg. */
WebkitBackgroundClip: "text",
WebkitTextFillColor: "transparent",
MozBackgroundClip: "text",
MozTextFillColor: "transparent",
fontSize: "50px"
}
P.S. I'm just now learning React, I may very well be missing something simple. Please let me know if you need more info.
Here is an example using Material UI v5.
The fill property of the icon is linked to the linear gradient's id property.
import OpenWithIcon from "#material-ui/icons/OpenWith"
const GradientOpenWithIcon = () => (
<>
<svg width={0} height={0}>
<linearGradient id="linearColors" x1={1} y1={0} x2={1} y2={1}>
<stop offset={0} stopColor="rgba(241,184,74,1)" />
<stop offset={1} stopColor="rgba(207,113,8,1)" />
</linearGradient>
</svg>
<OpenWithIcon sx={{ fill: "url(#linearColors)" }} />
</>
)

How to embed checkbox with label in Card Media?

I tried to code this thing. But the CardMedia will not go together with the checkbox. so responsive is a failure.
<Card>
<CardMedia
component='img'
alt=''
height='160'
image=''
title='Image'
style={{ backgroundColor: '#DEDBDB',
position: 'relative' }}
/>
{/*<input type='checkbox' id='select'*/}
{/* style={{ position: 'absolute', marginLeft: '20%', marginTop: '-2%'}}*/}
{/*/>*/}
{/*<label htmlFor='select'*/}
{/* style={{ position: 'absolute', marginLeft: '21%', marginTop: '-2.15%'}}*/}
{/*>選択</label>*/}
<Box mt={-6} ml={45}>
<span><Checkbox inputProps={{ 'aria-label': 'uncontrolled-checkbox' }} /></span>
</Box>
</Card>
I tried also the FormControlLabel for this so that the label and checkbox will be together and style it with position: absolute and some margins so that the result will be like this.
But the problem is that it is not responsive and if using box label disappear.
Thanks.
Ciao, your problem is connected to the zIndex of the label in FormControlLabel. Infact, if you inspect the page you can see the label present on DOM but invisible (maybe because on CardMedia the image is always on top, but this is my personal opinion).
To solve this problem, you can override the style of the label associated to the FormControlLabel. This is a codesandbox example.
At first I defined a CustomCheckbox:
const CustomCheckbox = withStyles((theme) => ({
root: {
// checkbox style example
// color: "#000000"
// '&$checked': {
// color: "#000000",
// },
},
checked: {}
}))((props) => <Checkbox color="default" {...props} />);
Then, I used it into Card:
<Box mt={-6} ml={45}>
<span>
<FormControlLabel
control={
<CustomCheckbox
checked={cheboxChecked}
onChange={handleChange}
name="toggleFavorite"
/>
}
label="Checkbox label" // label value
classes={{
label: styles.formcontrollabel // label class overriding
}}
/>
</span>
</Box>
And finally in makeStyles I made the override:
const useStyles = makeStyles(() => ({
formcontrollabel: {
"&.MuiFormControlLabel-label": {
zIndex: 1
}
}
}));
The result is:
The label is responsive also (in this case "label" word goes on new line if you reduce screen width) as long as possible (if you continue to reduce screen width, label will be cutted). But this is normal (because you defined Box like <Box mt={-6} ml={45}>). If you don't like this behaviour, you could use a Hidden component to hidden checkbox and label if screen goes under a certain breakpoint like:
<Hidden smDown> // if screen width goes under smDown breakpoint, the Hidden content will be hided
...
</Hidden>

Grid Items aren't beside eachother Material UI

I am using the Grid component in material-ui. I am having trouble having the grid items stack beside each other. Right now they are stacking below each other. I am not sure what is making them stack below each other. I have made it so only on small screens the grid items will stack on top of each other, other wise each grid item should take up 4 columns. I am using react for the front-end. Here is my code:
GridItem:
const styles = theme => ({
image: {
maxWidth: "100%",
maxHeight: "100%"
},
});
render() {
const { post, auth, classes } = this.props;
<div className={classes.root}>
<Link to={`/post/${post._id}`}>
<Grid
item
key={post.key}
sm={12}
md={4}
lg={4}
>
<img src={post.productImage} className={classes.image} />
<Typography>
{post.name}
<br />
{post.size}
</Typography>
</Grid>
</Link>
</div>
PostFeed:
render() {
const { posts } = this.props;
return posts.map(post => <ListingPost key={post._id} post={post} />);
}
}
Grid:
const styles = theme => ({
root: {
display: "flex",
flexWrap: "wrap",
justifyContent: "space-around",
overflow: "hidden",
backgroundColor: theme.palette.background.paper,
margin: 0
},
grid: {
margin: "auto",
width: "80%",
display: "inner-block"
},
paper: {
margin: "1%",
padding: "1%",
width: "80%"
},
});
render() {
const { classes } = this.props;
const { posts, loading } = this.props.post;
let postContent;
if (posts === null || loading) {
postContent = <div> loading</div>;
} else {
postContent = <PostFeed posts={posts} />;
}
return (
<div className={classes.root}>
<Paper className={classes.paper}>
<Typography align="center" variant="display2">
Listings
</Typography>
<Grid container className={classes.grid} spacing={16}>
{postContent}
</Grid>
</Paper>
</div>
);
}
}
Like Yash Rahurikar said, remove the line flexWrap: "wrap" of the Grid style, and add wrap='nowrap' to your Grid container.
You can try to use
<Grid container direction={'row'}></Grid>
so you inner items may arrange themselves beside each other.
Hope that helps
<Grid
container
direction="row"
justify="flex-start"
alignItems="flex-start"
spacing={1}
>
//above 👆🏻 should be your main grid container
//wrap your grid item with the link using component prop
<Grid item key={post.key} component={Link} to={`/post/${post._id}`} >
// Your rest content #here
</Grid>
</Grid>
Try using align-items property.
<Grid container spacing={1} alignItems="flex-end">

Categories

Resources