Use Refs In Material UI - reactjs

I'm trying to use Refs in Material UI to change Image src but it gives me an 'Undefined' error. It looks like the link is getting created but not being applied as the Images src, I feel like the problem lies in line 10 .
CodeSandBox - https://codesandbox.io/s/complexgrid-material-demo-forked-h6zfqh?file=/demo.tsx
const [loading] = useState(true);
const imageRef = React.useRef();
let txt = "IGO";
useEffect(() => {
imageRef.current.src = `https://flightaware.com/images/airline_logos/90p/${txt}.png`;
console.log(imageRef.current.src);
},
[loading, imageRef]);
<ButtonBase sx={{ width: 128, height: 128 }}>
<Img alt="complex" ref={imageRef} />
</ButtonBase>

You can use a list of references an change the image like you was doing.
I let you a functional code that it works like I think you want :)
import * as React from "react";
import { styled } from "#mui/material/styles";
import Grid from "#mui/material/Grid";
import D34, { useEffect, useState } from "react";
import Paper from "#mui/material/Paper";
import Typography from "#mui/material/Typography";
import ButtonBase from "#mui/material/ButtonBase";
import Data from "./abc.json";
const Img = styled("img")({
margin: "auto",
display: "block",
maxWidth: "100%",
maxHeight: "100%"
});
export default function ComplexGrid() {
const [loading] = useState(true);
const imagesRef = [];
let txt = "IGO";
useEffect(() => {
imagesRef.forEach((refImg) => {
refImg.src = `https://flightaware.com/images/airline_logos/90p/${txt}.png`;
});
/*imageRef.current.src = `https://flightaware.com/images/airline_logos/90p/${txt}.png`;
console.log(imageRef.current.src);*/
}, [loading]);
return (
<div className="hello">
{Data.response.map((post, posPost) => {
return (
<Paper
sx={{
pt: 1,
border: 1,
boxShadow: 0,
mt: 1,
maxWidth: 900,
flexGrow: 1,
backgroundColor: (theme) =>
theme.palette.mode === "dark" ? "#1A2027" : "#fff"
}}
>
<Grid container spacing={2}>
<Grid item>
<ButtonBase sx={{ width: 128, height: 128 }}>
<Img alt="complex" ref={(imageRef) => {
if (!imagesRef[posPost]) {
imagesRef.push(imageRef);
}
}} />
</ButtonBase>
</Grid>
<Grid item xs={12} sm container>
<Grid item xs container direction="column" spacing={2}>
<Grid item xs>
<Typography
gutterBottom
variant="subtitle1"
component="div"
>
Standard license
</Typography>
<Typography variant="body2" gutterBottom>
Full resolution 1920x1080 • JPEG
</Typography>
<Typography variant="body2" color="text.secondary">
ID: 1030114
</Typography>
</Grid>
<Grid item></Grid>
</Grid>
<Grid item>
<Typography
variant="subtitle1"
component="div"
sx={{ px: 2, p: 2 }}
>
$19.00
</Typography>
</Grid>
</Grid>
</Grid>
</Paper>
);
})}
</div>
);
}

you can use useState to change the src
const [img, setImg] = useState()
let txt = "IGO";
useEffect(() => {
setImg(`https://flightaware.com/images/airline_logos/90p/${txt}.png`)
}, [loading]);
{img && <Img alt="complex" src={img} />}

The main problem is the way to use ref.
At the moment, you are using same ref for same image component.
In order to manage the image tags well, you should use different ref for each image tab.
Please refer this code.
import * as React from "react";
import { styled } from "#mui/material/styles";
import Grid from "#mui/material/Grid";
import D34, { useEffect, useState } from "react";
import Paper from "#mui/material/Paper";
import RootRef from "#material-ui/core/RootRef";
import Typography from "#mui/material/Typography";
import ButtonBase from "#mui/material/ButtonBase";
import Data from "./abc.json";
import { red } from "#mui/material/colors";
const Img = styled("img")({
margin: "auto",
display: "block",
maxWidth: "100%",
maxHeight: "100%"
});
export default function ComplexGrid() {
const [loading] = useState(true);
const imageRef = React.useRef();
let txt = "IGO";
useEffect(() => {
console.log(imageRef.current.src);
if (imageRef.current.src === "")
imageRef.current.src = `https://flightaware.com/images/airline_logos/90p/${txt}.png`;
}, [loading, imageRef]);
return (
<div className="hello">
<Img alt="complex" ref={imageRef} />
{Data.response.map((post) => {
return (
<Paper
sx={{
pt: 1,
border: 1,
boxShadow: 0,
mt: 1,
maxWidth: 900,
flexGrow: 1,
backgroundColor: (theme) =>
theme.palette.mode === "dark" ? "#1A2027" : "#fff"
}}
>
<Grid container spacing={2}>
<Grid item>
<ButtonBase sx={{ width: 128, height: 128 }}>
{/* <Img alt="complex" ref={imageRef} /> */}
</ButtonBase>
</Grid>
<Grid item xs={12} sm container>
<Grid item xs container direction="column" spacing={2}>
<Grid item xs>
<Typography
gutterBottom
variant="subtitle1"
component="div"
>
Standard license
</Typography>
<Typography variant="body2" gutterBottom>
Full resolution 1920x1080 • JPEG
</Typography>
<Typography variant="body2" color="text.secondary">
ID: 1030114
</Typography>
</Grid>
<Grid item></Grid>
</Grid>
<Grid item>
<Typography
variant="subtitle1"
component="div"
sx={{ px: 2, p: 2 }}
>
$19.00
</Typography>
</Grid>
</Grid>
</Grid>
</Paper>
);
})}
</div>
);
}
Hope it would be helpful for you.
Thanks

Related

How to make a kind of synoptic table

Excuse me for having to ask, but the following topic is costing me a lot.
I want to make a table with connections, but I can't find the formula to do it, and I couldn't find any example, I tried to find an example and copy its code but it doesn't convince me either. This is what I want to do:
enter image description here
The idea is that, this is how I currently have it:
`
import React from "react";
import { useState, useEffect } from "react";
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 Button from "#material-ui/core/Button";
import Typography from "#material-ui/core/Typography";
import Grid from "#material-ui/core/Grid";
import Paper from "#material-ui/core/Paper";
import MundialButtons from "../../componentes/mundial-buttons";
const useStyles = makeStyles((theme) => ({
root: {
minWidth: 100,
maxWidth: 250,
flexGrow: 1
},
bullet: {
display: "inline-block",
margin: "0 2px",
transform: "scale(0.8)"
},
title: {
fontSize: 14
},
pos: {
marginBottom: 12
},
paper: {
padding: theme.spacing(2),
textAlign: "center",
color: theme.palette.text.primary
},
logo: {
float: "left"
}
}));
export default function Two() {
const url =
"https://adad1EUmIOwosuGTI7L2DD6S02RjOG7vbxU3FjVVD1u-iYiw/a!A1:Z1000";
const [todos, setTodos] = useState();
const fetchApi = async () => {
const response = await fetch(url);
const responseJSON = await response.json();
setTodos(responseJSON);
};
useEffect(() => {
fetchApi();
}, []);
const classes = useStyles();
const styleRed2 = [
{ marginTop: "90px" },
{ marginTop: "180px" },
{ marginTop: "270px" }
];
return (
<div id="all">
<MundialButtons />
<div className={classes.root}>
<Grid container spacing={3}>
<Grid item xs={12}>
<Paper className={classes.paper}>
<Typography>asd</Typography>
</Paper>
</Grid>
</Grid>
</div>
<div id="all">
{!todos
? "Cargando..."
: todos.map((todo, index) => {
return (
<Grid item xs={6}>
<div className={classes.root} id="octavos">
{todo.Local === undefined ||
todo.Local === "" ||
todo.Visitante === undefined ||
todo.Visitante === "" ? (
<div></div>
) : (
<Card className={classes.root} variant="outlined">
<CardContent>
<Typography
className={classes.title}
color="textSecondary"
gutterBottom
>
Octavos:
</Typography>
<Typography variant="h5" component="h2">
test
</Typography>
<Typography variant="h5" component="h2">
test
</Typography>
{/*<Typography
className={classes.pos}
color="textSecondary"
>
hora
</Typography>*/}
</CardContent>
</Card>
)}
<p></p>
</div>
</Grid>
);
})}
</div>
</div>
);
}
`
A box with connections between them
It's probably best to lean on open-source libraries for this, no need to reinvent the wheel. As it happens, what you require kind of already exists: https://www.npmjs.com/package/#g-loot/react-tournament-brackets. Demo.

how place a button at the center of the image on hover with Material UI (MUI) and reacts?

I have tried looking online and there are some examples of hovering using material ui and more specifically there are some examples using the CardMedia from MUI but I am having trouble adapting it to my case.
I want to add transparency to the existing picture and add a button in the center when the user hovers over the image.
But so far this is the code I have:
const [Hover, setHover] = useState(false);
const handleMouseEnter = () => {
setHover(true);
}
const handleMouseLeave = () => {
setHover(false);
}
const Butt = <Button variant="contained">Get a Free Quote</Button>
return (
<Box p={5}>
<Grid container spacing={5} justify="center">
{images.map((product, i) => {
return (
<Grid key={i} item xs={12} sm={6} md={4}>
<Card sx={{ minWidth: 200 }}>
<CardMedia
component="img"
height="200"
image={product.img}
alt="work portfolio"
onMouseOver={handleMouseEnter}
onMouseOut={handleMouseLeave}/>
</Card>
{Hover && (
<div>
<Butt/>
</div>
)}
</Grid>
);
})}
</Grid>
</Box>
)
using useState you can toggle between display=none to not display the button on onMouseLeave and display=block to display the button onMouseEnter
this is an example using your code :
import React, { useState } from "react";
import "./style.css";
import Grid from "#material-ui/core/Grid";
import Button from "#material-ui/core/Button";
import Box from "#material-ui/core/Box";
import Card from "#material-ui/core/Card";
import CardMedia from "#material-ui/core/CardMedia";
const Butt = ({ display }) => {
return (
<div className={display}>
<Button
style={{
position: "absolute",
top: "80%",
left: "50%",
transform: "translate(-50%, -50%)",
}}
variant="contained"
>
Get a Free Quote
</Button>
</div>
);
};
export default function App() {
const [display, setDisplay] = useState("notdisplayed");
const showButton = (e) => {
e.preventDefault();
setDisplay("displayed");
};
const hideButton = (e) => {
e.preventDefault();
setDisplay("notdisplayed");
};
return (
<Box p={5}>
<Grid container spacing={5} justifyContent="center">
<Grid item xs={12} md={4} sm={6}>
<Card
sx={{ minWidth: 200 }}
style={{ position: "relative", width: "100%" }}
>
<div
onMouseEnter={(e) => showButton(e)}
onMouseLeave={(e) => hideButton(e)}
>
<CardMedia
style={{
marginLeft: "auto",
marginRight: "auto",
width: "100%",
height: "auto",
zIndex: "1",
}}
component="img"
height="200"
image="https://st.depositphotos.com/1001894/3115/i/600/depositphotos_31157709-stock-photo-hassan-ii-mosque-in-casablanca.jpg"
alt="work portfolio"
/>
<Butt display={display} />
</div>
</Card>
</Grid>
</Grid>
</Box>
);
}
add style.css to make these styling in it :
.notdisplayed {
display: none;
}
.displayed {
display: block;
}
this is a demo in codesandbox .
You can achieve the same with just css. Have a look at the code below and this working codesandbox
What it does is just creating a Container that has the image and the Button. Then I styled the button to be hidden initially and is shown when hovering on the container (which is your Card).
import * as React from "react";
import { styled } from "#mui/material/styles";
import Card from "#mui/material/Card";
import CardMedia from "#mui/material/CardMedia";
import Grid from "#mui/material/Grid";
import Button from "#mui/material/Button";
const ButtonStyled = styled(Button)`
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: auto;
width: fit-content;
height: 40px;
display: none;
`;
const ContainerStyled = styled("div")`
position: absolute;
&:hover {
.test-button {
display: block;
}
}
}`;
export default function RecipeReviewCard() {
return (
<Grid container spacing={5} justify="center">
<Grid item xs={12} sm={6} md={4}>
<ContainerStyled>
<Card sx={{ minWidth: 200 }}>
<CardMedia
component="img"
height="200"
image="https://mui.com/static/images/cards/paella.jpg"
alt="work portfolio"
/>
</Card>
<ButtonStyled variant="contained" className="test-button">
Test button
</ButtonStyled>
</ContainerStyled>
</Grid>
<Grid item xs={12} sm={6} md={4}>
<ContainerStyled>
<Card sx={{ minWidth: 200 }}>
<CardMedia
component="img"
height="200"
image="https://mui.com/static/images/cards/paella.jpg"
alt="work portfolio"
/>
</Card>
<ButtonStyled variant="contained" className="test-button">
Test button 2
</ButtonStyled>
</ContainerStyled>
</Grid>
</Grid>
);
}
Let me know if it helps.

vertical divider not working in material ui

I have this component that contains a card and inside this card there are elements and I want to separate them through a vertical line and the problem is that the vertical line does not work.
const useStyles = makeStyles((theme: Theme) =>
createStyles({
orange: {
color: theme.palette.getContrastText(deepOrange[500]),
backgroundColor: deepOrange[500],
}
}),
);
const SpaceForm: FC = (props) => {
const classes = useStyles()
const workspaceData = useWorkspaceModule((state) => state.workspace)
console.log("inside component 1: ", workspaceData);
return (
<>
<Grid
container
spacing={3}
>
<Grid
item
lg={8}
md={6}
xs={12}
>
<Card>
<CardHeader title="Name your Workspace:"/>
<CardContent>
<Avatar style={{width: '3.4rem', height: '3.4rem'}} className={classes.orange}>N</Avatar>
{/*llll*/}
<Divider style={{ backgroundColor:'red'}} orientation="vertical" flexItem />
</CardContent>
</Card>
</Grid>
</Grid>
</>
);
};
export default SpaceForm;
You just wrap Avatar inside a flex Box and it will show Divider after Avatar:
<Box display="flex">
<Avatar
style={{ width: "3.4rem", height: "3.4rem" }}
className={classes.orange}
>
N
</Avatar>
{/*llll*/}
<Divider
style={{ backgroundColor: "red" }}
orientation="vertical"
flexItem
/>
</Box>

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;

Remove additional whitespace between row items with height (React JS/Material-UI)

I am trying to create a dynamic grid system. I have an dictionary and when any of these values are set to true, the component renders:
const sideBarCategories = [
{ id: "Test", comp: Test, default: false },
{ id: "Y", comp: Y, default: true },
{ id: "X", comp: X, default: false},
{ id: "Z", comp: Z, default: false},
];
So in this case, only the Y widget will be rendered. The render function is wrapped in a grid with direction row as follows:
<Grid
container
direction="row"
alignContent="center"
alignItems="center"
wrap="wrap"
spacing={4}
>
{cardsInGrid.map((card) => {
const Component = sideBarCategories.find(
(cat) => cat.id === card.id
).comp;
return <Component key={card.id} />;
})}
</Grid>
An example of the widget can be seen here:
import React from "react";
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 Button from "#material-ui/core/Button";
import Typography from "#material-ui/core/Typography";
import Grid from "#material-ui/core/Grid";
const useStyles = makeStyles({
root: {
minWidth: 275,
},
bullet: {
display: "inline-block",
margin: "0 2px",
transform: "scale(0.8)",
},
title: {
fontSize: 14,
},
pos: {
marginBottom: 12,
},
cardDesign: {
flexGrow: 1,
marginRight: "20px",
marginLeft: "20px",
width: "100%",
},
});
function Y() {
const classes = useStyles();
const bull = <span className={classes.bullet}>•</span>;
return (
<Grid item xs={12} sm={12} md={6} lg={6} xl={6}>
<div className={classes.root}>
<Card className={classes.cardDesign}>
<CardContent>
<Typography
className={classes.title}
color="textSecondary"
gutterBottom
>
Emoji Stats
</Typography>
<Typography variant="h5" component="h2">
be{bull}nev{bull}o{bull}lent
</Typography>
<Typography className={classes.pos} color="textSecondary">
adjective
</Typography>
<Typography variant="body2" component="p">
well meaning and kindly.
<br />
{'"a benevolent smile"'}
</Typography>
</CardContent>
<CardActions>
<Button size="small">Learn More</Button>
</CardActions>
</Card>
</div>
</Grid>
);
}
export default Y;
So in the row that I have, I make it so that there's a max of 2 items per row before it goes down and creates a new row (as seen by <Grid item xs={12} sm={12} md={6} lg={6} xl={6}> in the component file above.
The problem I am having is that if any of the components' height is larger than the other item in the same row, the website will account for that by having huge white spaces on either side of the smaller component. (See image below with orange representing whitespace)
I want it so that the components are directly below each other without that extra whitespace (see image below).
Sorry for the long post but any help with this matter would be appreciated.
I think the way you will have to do this is make two separate Grids with the cards mapped to even and odd indexes. Make sure the grids only allow 1 card per row.
<Grid
container
direction="row"
alignContent="center"
alignItems="center"
wrap="wrap"
spacing={4}
>
{cardsInGrid.filter((i, idx) => idx % 2).map((card) => {
const Component = sideBarCategories.find(
(cat) => cat.id === card.id
).comp;
return <Component key={card.id} />;
})}
</Grid>
<Grid
container
direction="row"
alignContent="center"
alignItems="center"
wrap="wrap"
spacing={4}
>
{cardsInGrid.filter((i, idx) => !(idx % 2)).map((card) => {
const Component = sideBarCategories.find(
(cat) => cat.id === card.id
).comp;
return <Component key={card.id} />;
})}
</Grid>
I would then wrap the entire thing in a flex container, with direction set to "row":
.dictionary-container {
display: flex;
flex-direction: row;
}
Hope this helps.

Resources