Too long title slide the menu button to the right outside of the card - javascript

I have a problem with MuiCardHeader
<CardHeader
disableTypography
avatar={renderAvatar()}
action={
<IconButton onClick={toggleMenu}>
<img src={MoreIcon} alt=""/>
</IconButton>
}
title={
<Typography noWrap variant="subtitle1">
{data.name}
</Typography>
}
subheader={
<Typography variant="subtitle2" color="textSecondary">
{data.children.items.length} items
</Typography>
}
/>
For some reason too long title or subtitle slide the menu button to the right outside the card.
How can I prevent it?
Result I need
Here is code sandbox
https://codesandbox.io/s/dazzling-paper-5d35h?file=/src/App.js
UPD: Solution
Add the following code
textOverflow: "ellipsis",
overflow: "hidden",
whiteSpace: "nowrap",
to .MuiCardHeader-content class
Thanks to everyone for help!

You need to restrict the parent with text-overflow: ellipsis, overflow: hidden and white-space: nowrap
So in your case you just have to add .MuiTypography-noWrap to the parent .MuiCardHeader-content

Related

Make elements on same line

I'm making a blog app using React, Material UI and Tailwindcss.
I made a grid of posts, but every postcard has a different height.
The question is: how to make all cards have the same height and all children in the same line?
Like This:
My Code:
Posts.jsx:
function Posts({ posts }) {
return (
<div className="mt-10">
<Container maxWidth="lg">
<Grid
container
rowSpacing={8}
columnSpacing={8}
direction="row"
justifyContent="center"
>
{posts.map((post, index) => (
<Grid key={index} item xs={12} sm={12} md={6} lg={4}>
<Card>
<CardActionArea>
<div className="flex flex-col ">
<CardHeader
avatar={
<Avatar>{post?.username?.substring(0, 1)}</Avatar>
}
title={post?.username}
subheader={"date"}
subheaderTypographyProps={{
color: "primary.dark",
}}
color="primary.main"
/>
<CardMedia
component="img"
height="194"
image={post?.photoURL}
/>
<CardContent>
<Typography variant="h6" color="primary.main">
{post.description}
</Typography>
</CardContent>
<Divider variant="middle" className="bg-[#fcfcfc]" />
<CardActions>
<IconButton>
<MdFavorite className="text-red-500" />
</IconButton>
</CardActions>
</div>
</CardActionArea>
</Card>
</Grid>
))}
</Grid>
</Container>
</div>
);
}
export default Posts;
You could try targeting the root <img> element directly by using the sx property. <CardMedia> would be updated like this:
...
<CardMedia
component="img"
height="194"
image={post?.photoURL}
sx={ {
['&.MuiCardMedia-root.MuiCardMedia-media.MuiCardMedia-img']: {
maxHeight: '194px'
}
} }
/>
...
As for making all children in the same line, you could:
set a maxHeight on the <CardHeader> and <CardContent> components, and
if you do not have control over the character length of the text in the header and content, use overflow: 'hidden', and textOverflow: 'ellipsis' to prevent long text strings from vertically expanding the <CardHeader> and <CardContent> components.
Try adding a fixed height on your tag. Also if the size of the images vary for each card it might cause a difference in the height of the whole card. If it doesn't work, try changing the height on your image to height: '194' to height: '194px'. Maybe this is what is causing an error.

div height doesn't adjust when Accordion is collapsed

I have two charts on top of each other that extend to the bottom of the screen. The first is collapsible via an Accordion.
However, if I do the following two things in sequence:
Make my browser window bigger
Then, collapse the Accordion (i.e., minimize the first graph).
Then there will be unwanted whitespace below the second graph.
<Flex direction="column" height="calc(100vh)" className="flex-wrapper">
<Box fontSize={["sm", "md", "lg", "xl"]}>Font Size</Box>
<Flex className="flex-wrapper0">
<div>123456789010</div>
<Box className="accordion-box-container">
<Accordion className="accordion-wrapper" allowToggle>
<AccordionItem>
<h2 className="accordion-title">
<AccordionButton
className="accordion-button"
borderRadius="md"
borderWidth="0px"
_focus={{ boxShadow: "none" }}
>
<Box
textAlign="left"
h={3}
_focus={{ boxShadow: "none" }}
></Box>
<AccordionIcon />
</AccordionButton>
</h2>
<AccordionPanel p="0">
<Box height="30vh">
<ThreeDataPoint />
</Box>
</AccordionPanel>
</AccordionItem>
</Accordion>
<div className="graph-wrapper">
<ThreeDataPoint />
</div>
</Box>
</Flex>
</Flex>
It seems like some interaction problem between browser resizing and css? I think I need to force a re-rendering of <ThreeDataPoint /> whenever the accordion button is pressed so that it can pick up the new height that it's supposed to be using. I wonder how to force such a re-rendering?
Here's codesandbox:
https://codesandbox.io/s/elegant-fermat-bugv1?file=/src/index.tsx
And the app URL:
https://bugv1.csb.app/
It was because of this !important flag causing troubles in the css file:
.graph-wrapper div {
height: 100% !important;
}
After commenting this out, it worked as expected.

Material Ui card change according to input

I have used material ui card and when i display it. It shows like below image. I want to show my card like that when cards do not fit the page it goes below and all card size must be same. How can i do that?
Here is my code:
const useStyles = makeStyles({ // Styling for my cards
root: {
width: 255,// i changed that line and below line but nothing happened
},
media: {
height: 190,
},
});
export default function AlbumsCard() {
const classes = useStyles();
..... //Some http requests
return (
<div>
<div>
<input
style={{
width: 500,
"margin-left": "auto",
"margin-right": "auto",
"margin-top": "10px",
}}
type="text"
placeholder="Search Album"
onChange={handleChange}
value={input}
/>
</div>
<div style={{ display: "flex" }}> //cardss
{k.map((album) => (
<Link
to={`/albums/${album.album_id}`}
style={{ textDecoration: "none", color: "#15cdfc" }}
>
<Card style={{ margin: "10px" }} className={classes.root}>
<CardActionArea>
<CardMedia
className={classes.media}
image={album.album_pfp}
title={album.album_name}
/>
<CardContent>
<Typography gutterBottom variant="h5" component="h2">
{album.album_name}
</Typography>
<Typography
variant="body2"
color="textSecondary"
component="p"
>
{album.album_song_count}
</Typography>
</CardContent>
</CardActionArea>
</Card>
</Link>
))}
</div>
</div>
);
}
Also the image from my app:
You can see that above. All cards placed in a line and some cards height's are different. Also there is a scroll bar.
I want to display whole card i a same size and when it overflows the page. It goes below.
Updated version:
Now each card has a different size how can i solve that? I think it is from image size.

I am trying to do a show more/show less text button

I am trying to make a show more/show less button. The actual version is not very good because I just sliced while state is false and left it untouched while it is true. Right now I want to show just the first row in each card but I don't really know how.
const classes = useStyles(props);
const { name, text, firstButtonText, secondButtonText } = props;
const [show, setShow] = useState(false);
const handleReadMore = () => {
setShow(prevShow => !prevShow);
};
return (
<Card className={classes.card}>
<CardMedia
className={classes.media}
image={require(`../../assets/photos/${name}.png`)}
title={name}
/>
<CardContent>
<Typography
gutterBottom
variant="h5"
component="h2"
className={classes.textStyling}
>
{show ? text : text.slice(0, 45)}
</Typography>
</CardContent>
<CardActions>
<Button size="small" color="primary">
{firstButtonText}
</Button>
<Button size="small" color="primary" onClick={handleReadMore}>
{secondButtonText}
</Button>
</CardActions>
</Card>
);
};
Try conditional render the items you want, like this:
<CardContent>
{show &&(<Typography
gutterBottom
variant="h5"
component="h2"
className={classes.textStyling}
>
{text}
</Typography>)}
</CardContent>
But then with your other rows.
Try conditional render the items you want, like this:
<CardContent>
<Typography gutterBottom variant="h5" component="h2" className={classes.textStyling}>
{text}
</Typography>
{show && (<div>...</div>)}
</CardContent>
You can use css to achieve this. Just set the line-height to the desired value, and max-height to multiples of that value you want to show.
.singleLine{ // 1 line
line-height:1.5em;
max-height:1.5em;
overflow: hidden;
}
.multiLine{ // 3 lines
line-height:1.5em;
max-height:4.5em;
overflow: hidden;
}
<Typography
gutterBottom
variant="h5"
component="h2"
className={show ? classes.multiLine : classes.singleLine}
>
.singleLine{
width: 50px;
line-height:1.5em;
max-height:1.5em;
overflow:hidden;
}
.multiLine{
width: 50px;
line-height:1.5em;
max-height:4.5em;
overflow:hidden;
}
<div>
Single -------------------------
</div>
<div class="singleLine">
dasdasasdasdas dasdasdasdasd sadasd asdasd sadasd sadasd asdasd a
</div>
<div>
Multi -------------------------
</div>
<div class="multiLine">
dasdasasdasdas dasdasdasdasd sadasd asdasd sadasd sadasd asdasd a
</div>

Using a ternary operator in a map function is rendering both results

I'm creating a vertical timeline with cards alternating sides as you move along the timeline, I'm trying to include a Popover effect to show more info about a person/event that fills the opposite whitespace on the timeline.
I'm trying to achieve this by using a ternary operator (using modulus to alternate sides in order) in my map callback, but it's rendering/returning both possible Popover results, onClick leads to a Popover popping on both sides of the card.
render() {
const cards = timelineObjects.map((card, i) => (
<React.Fragment key={i}>
{i % 2 === 0 ? (
<VerticalTimelineElement
className="vertical-timeline-element--work"
key={i}
iconStyle={{
background: "rgb(40,49,72)",
color: "#000"
}}
paddingTop="0em"
//icon={<Print/>}
>
<div>
<Card className="card">
<CardActionArea>
<CardMedia
style={{ height: 0, paddingTop: "100%" }}
image={card.image}
/>
<CardContent>
<Typography gutterBottom variant="h5" component="h2">
{card.title}
</Typography>
<Typography component="p">{card.subtitle}</Typography>
</CardContent>
</CardActionArea>
<Button
size="small"
color="primary"
component={Link}
//to={card.path}
onClick={this.handlePop}
>
Learn More, index: {i}, RIGHT
</Button>
<Popover
open={this.state.popped}
anchorEl={this.state.anchorEl}
anchorOrigin={{
horizontal: "right",
vertical: "center "
}}
transformOrigin={{
horizontal: "right",
vertical: "bottom"
}}
onClose={this.handleRequestClose}
>
Right popover text
</Popover>
</Card>
</div>
</VerticalTimelineElement>
)
:
(
<VerticalTimelineElement
className="vertical-timeline-element--work"
key={i}
iconStyle={{
background: "rgb(40,49,72)",
color: "#000"
}}
paddingTop="0em"
//icon={<Print/>}
>
<div>
<Card className="card">
<CardActionArea>
<CardMedia
style={{ height: 0, paddingTop: "100%" }}
image={card.image}
/>
<CardContent>
<Typography gutterBottom variant="h5" component="h2">
{card.title}
</Typography>
<Typography component="p">{card.subtitle}</Typography>
</CardContent>
</CardActionArea>
<Button
size="small"
color="primary"
component={Link}
//to={card.path}
onClick={this.handlePop}
>
Learn More, index : {i}, LEFT
</Button>
<Popover
open={this.state.popped}
anchorEl={this.state.anchorEl}
anchorOrigin={{
horizontal: "left",
vertical: "center "
}}
transformOrigin={{
horizontal: "left",
vertical: "bottom"
}}
onClose={this.handleRequestClose}
>
Left popover text
</Popover>
</Card>
</div>
</VerticalTimelineElement>
)}
</React.Fragment>
));
Here's a screen grab of the result.
Your popovers are all anchored to the same element (this.state.anchorEl) and are all configured to open based upon the same boolean (this.state.popped). This means if you have 2+ objects in your timeline, you render a popover for each object, and all popovers will be opened or closed together and all will be to the left/right of the only anchor element (whatever that is).
You should probably create a new TimelineObject component that renders a single timeline object and can have its own local state and assigns its own local anchorEl to anchor its popover to. Possibly its own popped state as well. Then your map function would be more like:
timelineObjects.map((card, i) => <TimelineObject key={i} card={card} onLeft={i%2==0} />)
Alternatively, instead of using this.state.popped as a boolean, use it as the card index to show the popup for. And in your Popover code do:
<Popover open={this.state.popped === i} ...
And when you set popped set it like this.setState({popped: indexOfCardToShowPopover, anchorEl: elementOfCardToAnchorPopover });
That way only 1 popover is ever open at a time.

Categories