How to enlarge a carousel MUI card on hover - reactjs

I'm creating a Carousel similar to Netflix's, where if you hover over a card, it will enlarge it and show more details.
My problem is that, instead of enlarging just the hovered card, it's enlarging all the other cards.
Here's the component:
import * as React from 'react';
import {useState} from 'react';
import Card from '#mui/material/Card';
import CardHeader from '#mui/material/CardHeader';
import CardMedia from '#mui/material/CardMedia';
import CardContent from '#mui/material/CardContent';
import CardActions from '#mui/material/CardActions';
import Avatar from '#mui/material/Avatar';
import IconButton from '#mui/material/IconButton';
import Typography from '#mui/material/Typography';
import {red} from '#mui/material/colors';
import FavoriteIcon from '#mui/icons-material/Favorite';
import ShareIcon from '#mui/icons-material/Share';
import {makeStyles} from '#mui/styles';
const ctCardHoveredSx = {
maxWidth: 345,
"&:hover": {},
"& .ct-card": {
display: "none"
},
"&:hover .ct-card": {
display: "flex"
},
"& .ct-card-panel": {
transition: "transform 0.15s ease-in-out"
},
"&:hover .ct-card-panel": {
transform: "scale3d(1.05, 1.05, 1)"
}
}
const useStyles = makeStyles({
root: {
maxWidth: 310,
transition: "transform 0.15s ease-in-out",
"& .ct-card": {
display: "none"
}
},
cardHovered: {
transform: "scale3d(1.05, 1.05, 1)",
"&:hover .ct-card": {
display: "flex"
}
}
});
const CtCard = () => {
const classes = useStyles();
const [hovered, setHovered] = useState<boolean>(false);
return (
<Card className={classes.root}
classes={{root: hovered ? classes.cardHovered : ""}}
onMouseOver={() => setHovered(true)}
onMouseOut={() => setHovered(false)}
>
<CardMedia
component="img"
height="194"
image="https://picsum.photos/200/300"
alt="Paella dish"
/>
<CardHeader
avatar={
<Avatar sx={{bgcolor: red[500]}} aria-label="recipe">
R
</Avatar>
}
title="Lorem Ipsum"
subheader="Lorem Ipsum"
/>
<CardContent className={'ct-card'}>
<Typography variant="body2" color="text.secondary">
This impressive paella is a perfect party dish and a fun meal to cook
together with your guests. Add 1 cup of frozen peas along with the mussels,
if you like.
</Typography>
</CardContent>
<CardActions disableSpacing className={'ct-card'}>
<IconButton aria-label="add to favorites">
<FavoriteIcon/>
</IconButton>
<IconButton aria-label="share">
<ShareIcon/>
</IconButton>
</CardActions>
</Card>
);
}
export default CtCard;
And how it's used:
<Stack
position={'relative'}
direction={'row'}
spacing={1}
>
{
Array(pageCount).fill(null).map((v, i) =>
<CtCard/>
)
}
</Stack>

Perhaps start by try giving each <CtCard/> being mapped a unique key:
{
Array(pageCount)
.fill(null)
.map((v, i) => <CtCard key={i} />);
}
The index is not a good option for key either, but use just for testing purposes and switch to some actual unique ID later.
Based on your description the events setting state do fire as expected, so it would seem that it might be the updating of the components causing the issue.

Related

What is wrong with my iconButton in React JS?

The console announce mistake at my IconButton open tag as unexpected token. i have installed material ui...i don't know what is the problem? can anyone help me? Thank you so much!
This is my product.js:
import React from 'react'
import {
Card,
CardMedia,
CardContent,
CardActions,
Typography,
IconButton
} from '#material-ui/core'
import {
AddShoppingCart
} from '#material-ui/icons'
import useStyles from './styles'
const Product = ({
product
}) => {
const classes = useStyles()
return (
<Card className={classes.root}>
<CardMedia className={classes.media} image='' title={product.name} />
<CardContent>
<div className={classes.cardContent}>
<Typography gutterBottom variant="h5" >
{product.name}
</Typography>
<Typography variant="h5" >
{product.price}
</Typography>
</div>
</CardContent>
<CardActions disableSpacing className={classes.cardActions}>
<IconButton aria-label="Add to Cart">
<AddShoppingCart />
</IconButton>
</CardActions>
</Card>
)
}
export default Product
This is my styles.js:
import {
makeStyles
} from '#material-ui/core/styles'
export default makeStyles(() => ({
{
root: {
maxWidth: '100%'
},
media: {
height: 0,
paddingTop: '56.25%'
},
cardActions: {
display: 'flex',
justifyContent: 'space-between'
},
}
}))
Btw i don't get where useStyles in my product.js come from when in my styles.js it's makeStyles not useStyles (i follow a tutorial on yt and he does so)
This: import useStyles from './styles' is named that because you/him wanted to, because you're exporting export default makeStyles.
export default means that you can import it with any name you want, its not a named export.
The problem maybe because here:
// You're returning an object of an object, instead of the inner { root: ... }.
({
{
root: {
maxWidth: '100%'
},
media: {
height: 0,
paddingTop: '56.25%'
},
cardActions: {
display: 'flex',
justifyContent: 'space-between'
},
}
}))
so maybe classes is not defined correctly, it would be needed the error stack to make sure where the error is, or at least a simple jsfiddle with the error would do.
There is nothing wrong with your IconButton component.
Some hints:
You can import your styles as classes right away, otherwise I doubt the class definitions will work at all.
Ensure you have installed #material-ui/icons
I took your code and did above changes:
import React from "react";
import {
Card,
CardMedia,
CardContent,
CardActions,
Typography,
IconButton
} from "#material-ui/core";
import { AddShoppingCart } from "#material-ui/icons";
import classes from "./styles";
const Product = ({ product }) => {
return (
<Card className={classes.root}>
<CardMedia className={classes.media} image="/" title={product.name} />
<CardContent>
<div className={classes.cardContent}>
<Typography gutterBottom variant="h5" component="h2">
{product.name}
</Typography>
<Typography gutterBottom variant="h5" component="h2">
{product.price}
</Typography>
</div>
</CardContent>
<CardActions disableSpacing className={classes.cardActions}>
<IconButton aria-label="Add to Cart">
<AddShoppingCart />
</IconButton>
</CardActions>
</Card>
);
};
export default Product;
See: https://codesandbox.io/s/ecstatic-sea-m9k33?file=/src/Product.js
we have used an arrow function to inline the event handler for out input field
So use
function Product({product}) {...}
instead of
const Product = ({ product }) => {}

OnClick for material UI components is not working in React Js

I am using Material UI components in my project. OnClick is not working for button. I have search on google also but didn't find any solution. I have tried using div also but that is also not working.
import React from 'react';
import { makeStyles } from '#material-ui/core/styles';
import AppBar from '#material-ui/core/AppBar';
import Toolbar from '#material-ui/core/Toolbar';
import Typography from '#material-ui/core/Typography';
import MobileDrawer from "./MobileDrawer";
import MenuIcon from '#material-ui/icons/Menu';
import IconButton from "#material-ui/core/IconButton";
const useStyles = makeStyles((theme) => ({
root: {
flexGrow: 1,
},
menuButton: {
marginRight: theme.spacing(2),
},
title: {
flexGrow: 1,
marginLeft: "20px",
marginTop: "8px"
},
}));
export default function MobileHeader() {
const classes = useStyles();
const handleDrawerStatus = () => {
alert("hi")
}
return (
<div className={classes.root}>
<AppBar position="static" style={{ backgroundColor: "#040b2d" }}>
<Toolbar>
<IconButton
color="inherit"
aria-label="open drawer"
edge="start"
onClick={handleDrawerStatus}
>
<MenuIcon />
</IconButton>
</Toolbar>
</AppBar>
</div>
);
}

Conditional Rendering in Nextjs & TypeScript not working

Trying to render a component conditionally. I have a drawHelper variable & a function to toggle it true or false. The component renders or not based on the initial value of drawHelper. (false, doesn't render, true it does).
The toggle function changes the value. I checked with console.log(drawHelper) But changing the value does not make the component appear or disappear.
Am I missing something here?
import React from 'react';
import AppBar from '#material-ui/core/AppBar';
import CssBaseline from '#material-ui/core/CssBaseline';
import Divider from '#material-ui/core/Divider';
import Drawer from '#material-ui/core/Drawer';
import Hidden from '#material-ui/core/Hidden';
import IconButton from '#material-ui/core/IconButton';
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 DashboardIcon from '#material-ui/icons/Dashboard';
import MenuIcon from '#material-ui/icons/Menu';
import Toolbar from '#material-ui/core/Toolbar';
import Typography from '#material-ui/core/Typography';
import { makeStyles, useTheme, Theme, createStyles } from '#material-ui/core/styles';
import { User } from './User';
import { Draw } from "components/Layout/Countryballs/Draw";
const drawerWidth = 240;
export const useStyles = makeStyles((theme: Theme) =>
createStyles({
root: {
display: 'flex',
color: '#fff',
},
drawer: {
[theme.breakpoints.up('sm')]: {
width: drawerWidth,
flexShrink: 0,
},
},
appBar: {
marginLeft: drawerWidth,
[theme.breakpoints.up('sm')]: {
width: `calc(100% - ${drawerWidth}px)`,
},
},
menuButton: {
marginRight: theme.spacing(2),
[theme.breakpoints.up('sm')]: {
display: 'none',
},
},
toolbar: theme.mixins.toolbar,
drawerPaper: {
width: drawerWidth,
backgroundColor: theme.palette.primary.main
},
content: {
flexGrow: 1,
},
menuItem: {
color: '#fff',
},
}),
);
export const Layout: React.FC<LayoutProps> = (props) => {
const { container } = props;
const classes = useStyles();
const theme = useTheme();
const [mobileOpen, setMobileOpen] = React.useState(false);
function handleDrawerToggle() {
setMobileOpen(!mobileOpen);
}
// Display Draw component
// 1 Create property
var drawHelper: Boolean = false;
function toggleDraw() {
console.log(drawHelper);
drawHelper = !drawHelper;
}
const drawer = (
<div>
<div className={classes.toolbar} />
<Divider />
<List>
{['Draw'].map((text) => (
<ListItem button key={text} onClick={toggleDraw} className={classes.menuItem}>
<ListItemIcon className={classes.menuItem}><DashboardIcon /></ListItemIcon>
<ListItemText primary={text} />
</ListItem>
))}
</List>
</div>
);
return (
<div className={classes.root}>
<CssBaseline />
<AppBar position="fixed" className={classes.appBar}>
<Toolbar>
<IconButton
color="inherit"
aria-label="open drawer"
edge="start"
onClick={handleDrawerToggle}
className={classes.menuButton}
>
<MenuIcon />
</IconButton>
<Typography variant="h6" noWrap>
Project name
</Typography>
<User/>
</Toolbar>
</AppBar>
<nav className={classes.drawer} aria-label="mailbox folders">
{/* The implementation can be swapped with js to avoid SEO duplication of links. */}
<Hidden smUp implementation="css">
<Drawer // this one is for mobile
container={container}
variant="temporary"
anchor={theme.direction === 'rtl' ? 'right' : 'left'}
open={mobileOpen}
onClose={handleDrawerToggle}
classes={{
paper: classes.drawerPaper,
}}
ModalProps={{
keepMounted: true, // Better open performance on mobile.
}}
>
{drawer}
</Drawer>
</Hidden>
<Hidden xsDown implementation="css">
<Drawer // This one is for desktop
classes={{
paper: classes.drawerPaper,
}}
variant="permanent"
open
>
{drawer}
</Drawer>
</Hidden>
</nav>
<main className={classes.content}>
{/* This is where my components renders */}
{
drawHelper === true && (<Draw/>)
}
<div className={classes.toolbar} />
{props.children}
</main>
</div>
);
}
The variable drawHelper in your code is instantiated on every render. You'd want to use React's state to make sure your drawHelper's value is preserved on the next re-renders.
const [drawHelper, toggleDrawHelper] = React.useState(false)
function toggleDraw() {
toggleDrawHelper(!drawHelper);
}

How can I reuse code wrapping Material-UI's Grid while varying the number of columns?

I would like the second grid block under the title "Other Projects of Note" to be mapped through as a 3 column grid. How can I do this without creating a new component? Material-UI controls it's columns with the grid item xs={12} sm={6} and on the 3 column grid I'd need it to read as grid item xs={12} sm={6} lg={4}.
It seems like I'd be copying and pasting the <Card /> component and renaming it to achieve this. I'd like to avoid that. Demo below:
codesandbox
Here's the code for my current Card component:
import React from 'react';
import Grid from '#material-ui/core/Grid';
import Card from '#material-ui/core/Card';
import CardActionArea from '#material-ui/core/CardActionArea';
import CardActions from '#material-ui/core/CardActions';
import { makeStyles } from '#material-ui/core/styles';
import { loadCSS } from 'fg-loadcss';
import CardContent from '#material-ui/core/CardContent';
import CardMedia from '#material-ui/core/CardMedia';
import Button from '#material-ui/core/Button';
import Typography from '#material-ui/core/Typography';
import Box from '#material-ui/core/Box';
import Icon from '#material-ui/core/Icon';
import GitHubIcon from '#material-ui/icons/GitHub';
import Tooltip from '#material-ui/core/Tooltip';
import Zoom from '#material-ui/core/Zoom';
import Link from '#material-ui/core/Link';
const useStyles = makeStyles((theme) => ({
root: {
maxWidth: '100%',
flexGrow: 1,
},
cardGrid: {
paddingTop: theme.spacing(2),
},
media: {
height: 340,
},
button: {
margin: theme.spacing(1),
},
arrow: {
color: theme.palette.common.black,
},
tooltip: {
backgroundColor: theme.palette.common.black,
},
icons: {
'& > .fab': {
marginRight: theme.spacing(4),
},
margin: '1rem 0',
},
}));
function TwoCard(props) {
const classes = useStyles();
React.useEffect(() => {
const node = loadCSS(
'https://use.fontawesome.com/releases/v5.12.0/css/all.css',
document.querySelector('#font-awesome-css')
);
return () => {
node.parentNode.removeChild(node);
};
}, []);
return (
<>
<Grid item xs={12} sm={6}>
<Card>
<CardActionArea>
<CardMedia
className={classes.media}
image={props.card.image}
title='Contemplative Reptile'
/>
<CardContent className={classes.content}>
<Typography gutterBottom variant='h5' component='h2'>
{props.card.project}
</Typography>
<Typography variant='subtitle1' gutterBottom>
{props.card.framework}
</Typography>
<Typography gutterBottom variant='body2' component='p'>
{props.card.description}
</Typography>
<Box className={classes.icons}>
<Typography gutterBottom variant='subtitle2'>
TOOLS USED
</Typography>
<Tooltip
TransitionComponent={Zoom}
arrow
title='REACT'
aria-label='react'
>
<Icon className='fab fa-react fa-3x' color='primary' />
</Tooltip>
<Tooltip
TransitionComponent={Zoom}
arrow
title='HTML5'
aria-label='add'
>
<Icon className='fab fa-html5 fa-3x' color='primary' />
</Tooltip>
<Tooltip
TransitionComponent={Zoom}
arrow
title='CSS3'
aria-label='add'
>
<Icon className='fab fa-css3 fa-3x' color='primary' />
</Tooltip>
</Box>
</CardContent>
</CardActionArea>
<CardActions>
<Button variant='outlined' size='small' color='primary'>
<Link
href={props.card.projectUrl}
target='_blank'
rel='noopener noreferrer'
className={classes.links}
underline='none'
color='inherit'
>
View Project
</Link>
</Button>
<Button
variant='outlined'
size='small'
color='primary'
className={classes.button}
endIcon={<GitHubIcon />}
>
<Link
href={props.card.githubUrl}
target='_blank'
rel='noopener noreferrer'
underline='none'
color='inherit'
>
Code
</Link>
</Button>
</CardActions>
</Card>
</Grid>
</>
);
}
export default TwoCard;
and here's the code that uses that Card component:
import React from "react";
import Grid from "#material-ui/core/Grid";
import Card from "./Card.js";
import { Typography } from "#material-ui/core";
import { makeStyles } from "#material-ui/core/styles";
const useStyles = makeStyles((theme) => ({
headings: {
padding: "20px 0"
}
}));
function Projects(props) {
const classes = useStyles();
let cards = props.data.map((card, i) => {
return <Card card={card} key={i} />;
});
return (
<>
<Typography variant="h4" gutterBottom>
Featured Projects
</Typography>
<Grid container spacing={2}>
{cards}
</Grid>
<Typography variant="h4" className={classes.headings}>
Other Projects of note
</Typography>
<Grid container spacing={2}>
{cards}
</Grid>
</>
);
}
export default Projects;
You can pass a prop to your Card component to control whether it is two-column or three-column. For instance, you can pass a maxColumns prop and use it in the following manner:
<Grid item xs={12} sm={6} lg={maxColumns > 2 ? 4 : undefined}>
Then your Projects component can pass the prop (in my example, I'm defaulting maxColumns to 2, so it doesn't need to be specified in that case):
import React from "react";
import Grid from "#material-ui/core/Grid";
import Card from "./Card.js";
import { Typography } from "#material-ui/core";
import { makeStyles } from "#material-ui/core/styles";
const useStyles = makeStyles((theme) => ({
headings: {
padding: "20px 0"
}
}));
function Projects(props) {
const classes = useStyles();
let twoColumnCards = props.data.map((card, i) => {
return <Card card={card} key={i} />;
});
let threeColumnCards = props.data.map((card, i) => {
return <Card maxColumns={3} card={card} key={i} />;
});
return (
<>
<Typography variant="h4" gutterBottom>
Featured Projects
</Typography>
<Grid container spacing={2}>
{twoColumnCards}
</Grid>
<Typography variant="h4" className={classes.headings}>
Other Projects of note
</Typography>
<Grid container spacing={2}>
{threeColumnCards}
</Grid>
</>
);
}
export default Projects;

Material UI Grid keeps displaying as a column

I have been working with Material UI grid and I am trying to display data in a grid of cards that show in rows as opposed to a single column. My grid currently displays like this stacked in a column(please ignore the size of the cards this is just a zoomed in screenshot):
I would like the grid to display like this in rows as opposed to a single column:
I feel like I'm missing something simple but I'm just not sure why it keeps stacking in a single column. The code is as follows:
import React, { useState, useEffect } from "react"
// components
import Grid from "#material-ui/core/Grid"
import { makeStyles } from "#material-ui/core/styles"
import axios from "axios"
import RepoCard from "../components/RepoCard"
import Container from "#material-ui/core/Container"
// context
const Profile = () => {
const [data, setData] = useState(null)
const fetchGit = () => {
axios
.get(`https://api.github.com/users/rterrell25/repos?`)
.then((res) => {
setData(res.data)
console.log(res.data)
})
.catch((err) => console.log(err))
}
useEffect(() => {
fetchGit()
}, [])
return (
<div style={{ marginTop: 85 }}>
{!data ? (
<h1>Loading...</h1>
) : (
data.map((user) => (
<Container>
<Grid container spacing={4}>
<RepoCard name={user.name} />
</Grid>
</Container>
))
)}
</div>
)
}
export default Profile
the Repo Card Component code:
import React from "react"
import { Link } from "react-router-dom"
// MUI stuff
import Button from "#material-ui/core/Button"
import { makeStyles } from "#material-ui/core/styles"
import Card from "#material-ui/core/Card"
import CardActions from "#material-ui/core/CardActions"
import CardContent from "#material-ui/core/CardContent"
import CardMedia from "#material-ui/core/CardMedia"
import Grid from "#material-ui/core/Grid"
import Typography from "#material-ui/core/Typography"
const useStyles = makeStyles((theme) => ({
icon: {
marginRight: theme.spacing(2),
},
heroContent: {
backgroundColor: theme.palette.background.paper,
padding: theme.spacing(8, 0, 6),
},
heroButtons: {
marginTop: theme.spacing(4),
},
cardGrid: {
paddingTop: theme.spacing(8),
paddingBottom: theme.spacing(8),
},
card: {
height: "100%",
display: "flex",
flexDirection: "column",
},
cardMedia: {
paddingTop: "56.25%", // 16:9
},
cardContent: {
flexGrow: 1,
},
footer: {
backgroundColor: theme.palette.background.paper,
padding: theme.spacing(6),
},
}))
const MovieCard = ({ name }) => {
const classes = useStyles()
return (
<Grid item xs={12} sm={6} md={4}>
<Card className={classes.card}>
<CardMedia
className={classes.cardMedia}
image={"#"}
title='Movie nane'
/>
<CardContent className={classes.cardContent}>
<Typography gutterBottom variant='h5' component='h2'>
"Movie name"
</Typography>
<Typography>Genre: Comedy</Typography>
<Typography noWrap>"Movie Summary"</Typography>
</CardContent>
<CardActions>
<Link to={""}>
<Button size='small' color='primary'>
Details
</Button>
</Link>
</CardActions>
</Card>
</Grid>
)
}
export default MovieCard
Change the xs={12} to xs={4}
The grid creates visual consistency between layouts while allowing flexibility across a wide variety of designs. Material Design’s responsive UI is based on a 12-column grid layout.
Refer: document of Material-UI Grid

Resources