Trippled Image Effect on hover with MUI - javascript

There is this Codepen with a working tripple hover effect on an image: Codepen
When I try to rebuild it as styled component on React Typescript with MUI and MUI Image accepted by MUI I am getting an error in my style={...} and cant get it to work in my React App. Can anyone help why? I declared my css in a const styles and apply it inline.
App.tsx
import React from 'react';
import { Box } from '#mui/material';
import Image from 'mui-image';
const styles = {
body:{
background: "#3d4552",
fontFamily: "arial",
fontSize: "12px",
color: "#fff",
},
img: {
border: "1px solid #d2d2d2",
padding: "3px",
boxShadow: "0 0 15px rgba(0,0,0,.1)",
},
picContainer: {
maxWidth: "210px",
maxHeight: "210px",
margin: "50px",
},
pic: {
background: "#fff",
position: "absolute",
transition: "all 0.2s ease",
backfaceVisibility:"hidden",
},
"pix:nth:child(1)": {
zIndex: 3,
},
"pic:nth-child(2)": {
zIndex: 1,
},
"pic:nth-child(3)": {
zIndex: 2,
},
"picContainer:hover .pic:nth-child(1)": {
transform: "rotate(15deg)",
transition: "all 0.5s ease",
},
"picContainer:hover .pic:nth-child(2)": {
transform: "rotate(7deg)",
transition: "all 0.10s ease",
},
"picContainer:hover .pic:nth-child(3)": {
transform: "rotate(-5deg)",
},
picCaption: {
background: "#82a3d4",
padding: "10px 15px",
color: "#fff",
fontSize: "12px",
position: "relative",
zIndex: "10",
top: "90px",
left: "200px",
borderRadius: "3px",
transition: "all 0.5s ease",
opacity: 0,
},
"picCaption:hover": {
background: "#607fae",
},
"picContainer:hover .pic-caption": {
left:"230px",
opacity: 1,
},
};
function Hover() {
return (
<Box style={styles.picContainer}>
<Image src="https://via.placeholder.com/150" style={styles.pic} />
<Image src="https://via.placeholder.com/150" style={styles.pic} />
<Image src="https://via.placeholder.com/150" style={styles.pic} />
</Box>
);
}
export { Hover };
The error:
(property) MuiImageProps.style?: React.CSSProperties | undefined
Type '{ background: string; position: string; transition: string; backfaceVisibility: string; }' is not assignable to type 'Properties<string | number, string & {}>'.
Types of property 'backfaceVisibility' are incompatible.
Type 'string' is not assignable to type 'BackfaceVisibility | undefined'.ts(2322)
index.d.ts(28, 5): The expected type comes from property 'style' which is declared here on type 'IntrinsicAttributes & MuiImageProps & { children?: ReactNode; }'

First of all, I minified the styles object by removing unused or erroneous styles such as picCaption or pix:nth:child(1). I've also replaced all occurrences of :nth-child() with :nth-of-type() since nth-child is potentially unsafe.
For this solution there are only two relevant style objects, picContainer which will be applied to the outer <Box/> and img for the mui <Image/> wrapper. The pic class can be referenced inside the styling object. We can make use of nesting to avoid repetition, analogous to scss this also works here.
const styles = {
picContainer: {
maxWidth: "210px",
maxHeight: "210px",
margin: "50px",
"&:hover": {
".pic:nth-of-type(1)": {
transform: "rotate(15deg)",
transition: "all 0.5s ease",
},
".pic:nth-of-type(2)": {
transform: "rotate(7deg)",
transition: "all 0.10s ease",
},
".pic:nth-of-type(3)": {
transform: "rotate(-5deg)",
},
},
".pic": {
position: "absolute",
transition: "all 0.2s ease",
backfaceVisibility: "hidden",
"&:nth-of-type(2)": {
zIndex: 1,
},
"&:nth-of-type(3)": {
zIndex: 2,
},
},
},
img: {
backgroundColor: "#fff",
border: "1px solid #d2d2d2",
padding: "3px",
boxShadow: "0 0 15px rgba(0,0,0,.1)",
},
};
Now since its my first time working with <Image/> from mui-image I found it tricky here. The image gets wrapped inside a MUI image class which comes with its own styling. Using wrapperClassName and custom width, height was necessary here to avoid the image from being expanded and stretched.
// Hover.tsx
return (
<Box sx={styles.picContainer}>
<Image
wrapperClassName="pic"
width={150}
height={150}
src="https://via.placeholder.com/150"
style={styles.img}
/>
<Image
wrapperClassName="pic"
width={150}
height={150}
src="https://via.placeholder.com/150"
style={styles.img}
/>
<Image
wrapperClassName="pic"
width={150}
height={150}
src="https://via.placeholder.com/150"
style={styles.img}
/>
</Box>
);
The result is identical to the Codepen that has been provided in the question:

Related

What is the proper way of importing SVG image as a React component?

I have the following code. Importing an SVG image as a component. A webpack loader allows me to import SVGs.
var MouseHighlight = (props: Properties) => {
return (
<ReactShadowRoot>
<Box className="quizmeai-body">
<Box ... >
<Button ... >
<Logo
style={{
height: "20px",
width: "20px",
left: "-21px",
display: "block",
position: "absolute",
top: "-1px",
boxShadow: "0px 8px 15px rgb(0 0 0 / 40%)",
transition: "all 0.3s ease 0s",
zIndex: "5",
}}
/>
<Box>
...
</Box>
</Button>
</Box>
</Box>
</ReactShadowRoot>
);
};
When run npm run build, the compiler complains with the following.
ERROR in /Users/murat/Desktop/quizme/extension/src/contentScripts/example.tsx
./src/contentScripts/example.tsx 162:14-19
[tsl] ERROR in /Users/murat/Desktop/quizme/extension/src/contentScripts/example.tsx(162,15)
TS2322: Type '{ style: { height: string; width: string; left: string; display: string; position: string; top: string; boxShadow: string; transition: string; zIndex: string; }; }' is not assignable to type 'IntrinsicAttributes'.
Property 'style' does not exist on type 'IntrinsicAttributes'.
How can I import import the SVG image properly? Thanks.
I couldn't really try anything. I just started working on this code and I'm a little lost.

My Card's Background and Image are flickering when I move over it (while hovering), it works fine when I just hover over it and stop moving my cursor

import React from 'react';
import { makeStyles, Typography, Grid } from '#material-ui/core';
import Card from '#material-ui/core/Card';
const styles = makeStyles((theme) => ({
card: {
background: 'black',
width: '140px',
margin: '0px 10px',
},
root: {
width: '142px',
height: '196px',
overflow: 'hidden',
position: 'relative',
transition: 'all 0.5s ease-out',
},
image: {
position: 'absolute',
width: '100%',
height: '100%',
objectFit: 'cover',
transition: 'all 0.5s ease-out',
zIndex: 10,
'&:hover': {
zIndex: -1,
},
},
textPositioning: {
width: '140px',
height: '40px',
overflow: 'hidden',
marginTop: '10px',
},
text: {
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
fontFamily: 'Comfortaa',
},
genresContainer: {
backgroundColor: theme.palette.primary.main,
width: '100%',
height: '100%',
position: 'relative',
transition: 'all 0.5s ease-out',
zIndex: 2,
},
genreList: {
width: '100%',
height: '100%',
},
}));
interface AnimeData {
image_url: string;
title: string;
genres: Array<any>;
}
interface DisplayAnimeCardProps {
data: AnimeData;
}
const DisplayAnimeCard: React.FC<DisplayAnimeCardProps> = (
props: DisplayAnimeCardProps
) => {
const classes = styles();
const { data } = props;
console.log(typeof data, data.genres);
return (
<Card className={classes.card}>
<div className={classes.root}>
<img
className={classes.image}
alt='anime'
src={data.image_url}
/>
<div className={classes.genresContainer}>
<Grid
className={classes.genreList}
container
direction='column'
justifyContent='center'
alignItems='center'
>
{data.genres.map((genre) => (
<Typography
className={classes.text}
variant='body1'
>
{genre.name}
</Typography>
))}
</Grid>
</div>
</div>
<div className={classes.textPositioning}>
<Typography className={classes.text} variant='body1'>
{data.title}
</Typography>
</div>
</Card>
);
};
export default DisplayAnimeCard;
The Hovering effect show a div a top the image with some text. It works fine when I simply hover over the Card and do not move the cursor again. But If I move my cursor(during hovering) a top the card, the hover effect breaks and causes flickering between the textPositioningdiv and the image. I want to stop this flickering and just show the textPositioning div whenever I hover over the card and move my cursor

How to keep text on sceen while scrolling in react

I am working on a very simple website consisting of an image, with some text overlaid on top of it. In some cases, the image may be bigger than the screen, and then the user can scroll. However, I would like the overlaid text to remain on-screen, centered, even when the user scrolls. How can I do this?
Here is a basic idea of my code at the moment:
const styles = theme => ({
main_screen: {
position: "relative",
textAlight: "center",
color: "white",
height: '100%',
width: '100%',
margin: "0",
padding: "0"
},
caption: {
color: "yellow",
margin: "1em",
},
overlayText: {
position: "absolute",
width: "100%",
top: "5%",
alignItems:"center"
},
main_image: {
maxwidth: "100%",
maxHeight: "100%",
objectFit:"cover",
display: "block",
margin: "0",
padding: "0",
overflow: "scroll"
},
})
class ExampleSite extends React.Component {
....
render(){
return(
<div id="main-screen" className={classes.main_screen}>
<img id='target-image' onLoad={this.readDims} className={classes.main_image} src={pano_1} alt={""}/>
<div className={classes.overlayText}>
<Typography className={classes.caption} align="center" > {this.state.instructionText} </Typography>
</div>
</div>
)
}
}
position: fixed; on the text object is what you want. MDN - CSS Position

Setting imported material icon as a background in jss

I have a React-mui draggable dialog component on which I am using resizable box.
const styles = theme => ({
resizable: {
position: "relative",
"& .react-resizable-handle": {
position: "absolute",
width: 20,
height: 20,
bottom: 0,
right: 0,
background:
"url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2IDYiIHN0eWxlPSJiYWNrZ3JvdW5kLWNvbG9yOiNmZmZmZmYwMCIgeD0iMHB4IiB5PSIwcHgiIHdpZHRoPSI2cHgiIGhlaWdodD0iNnB4Ij48ZyBvcGFjaXR5PSIwLjMwMiI+PHBhdGggZD0iTSA2IDYgTCAwIDYgTCAwIDQuMiBMIDQgNC4yIEwgNC4yIDQuMiBMIDQuMiAwIEwgNiAwIEwgNiA2IEwgNiA2IFoiIGZpbGw9IiMwMDAwMDAiLz48L2c+PC9zdmc+')",
"background-position": "bottom right",
padding: "0 3px 3px 0",
"background-repeat": "no-repeat",
"background-origin": "content-box",
"box-sizing": "border-box",
cursor: "se-resize"
}
}
});
return (
<StyledDialog
open={open}
classes={{root: classes.dialog, paper: classes.paper}}
PaperComponent={PaperComponent}
aria-labelledby="draggable-dialog"
>
<ResizableBox
height={520}
width={370}
minConstraints={[300, 500]}
maxConstraints={[Infinity, Infinity]}
className={classes.resizable}
>
<DialogContent classes={{root: classes.dialogContent}} id="draggable-dialog">
<IconButton className={classes.clearIcon} aria-label="Clear" onClick={onClose}>
<ClearIcon/>
</IconButton>
<iframe
src={hjelpemiddel.url}
title={hjelpemiddel.navn}
width="100%"
height="500px">
</iframe>
</DialogContent>
</ResizableBox>
</StyledDialog>
);
}
I would like to use my own icon instead of the default image used for the ResizableBox. How can I set the icon that I am importing from material icons as background of resizable?
import ZoomOutMapIcon from '#material-ui/icons/ZoomOutMap';
Material-UI is using SVG for the ZoomOutMapIcon. What I would do is convert that SVG to base64 then apply that to the background-url
This is its base64 output: PHN2ZyBjbGFzcz0iTXVpU3ZnSWNvbi1yb290IiBmb2N1c2FibGU9ImZhbHNlIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgYXJpYS1oaWRkZW49InRydWUiID48cGF0aCBkPSJNMTUgM2wyLjMgMi4zLTIuODkgMi44NyAxLjQyIDEuNDJMMTguNyA2LjcgMjEgOVYzaC02ek0zIDlsMi4zLTIuMyAyLjg3IDIuODkgMS40Mi0xLjQyTDYuNyA1LjMgOSAzSDN2NnptNiAxMmwtMi4zLTIuMyAyLjg5LTIuODctMS40Mi0xLjQyTDUuMyAxNy4zIDMgMTV2Nmg2em0xMi02bC0yLjMgMi4zLTIuODctMi44OS0xLjQyIDEuNDIgMi44OSAyLjg3TDE1IDIxaDZ2LTZ6Ij48L3BhdGg+PC9zdmc+
and I usually use makeStyles for overriding CSS when using Material UI
const useStyles = makeStyles({
resizable: {
position: "relative",
"& .react-resizable-handle": {
position: "absolute",
width: 20,
height: 20,
bottom: 0,
right: 0,
background:
"url('data:image/svg+xml;base64,PHN2ZyBjbGFzcz0iTXVpU3ZnSWNvbi1yb290IiBmb2N1c2FibGU9ImZhbHNlIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgYXJpYS1oaWRkZW49InRydWUiID48cGF0aCBkPSJNMTUgM2wyLjMgMi4zLTIuODkgMi44NyAxLjQyIDEuNDJMMTguNyA2LjcgMjEgOVYzaC02ek0zIDlsMi4zLTIuMyAyLjg3IDIuODkgMS40Mi0xLjQyTDYuNyA1LjMgOSAzSDN2NnptNiAxMmwtMi4zLTIuMyAyLjg5LTIuODctMS40Mi0xLjQyTDUuMyAxNy4zIDMgMTV2Nmg2em0xMi02bC0yLjMgMi4zLTIuODctMi44OS0xLjQyIDEuNDIgMi44OSAyLjg3TDE1IDIxaDZ2LTZ6Ij48L3BhdGg+PC9zdmc+')",
"background-position": "bottom right",
padding: "0 3px 3px 0",
"background-repeat": "no-repeat",
"background-origin": "content-box",
"box-sizing": "border-box",
cursor: "se-resize"
}
}
});
<ResizableBox
className={classes.resizable}
>

In material ui using makeStyles, is it possible to write a css rule that applies only if the element has both classes?

I know that I can do this in CSS.
.makeStyles-mainNavWrapper-67.sticky{
position: fixed;
top: 0px;
opacity: 1;
transition: opacity 1s ease;
padding: 10px;
}
I want to know if I can do this in Material-UI so that I do not have to have two separate stylesheets so to speak (one for the MaterialUI ReactApp and one that is linked in the HTML tag.
const Header = (props) => {
const useStyles = makeStyles(theme => ({
mainNav: {
zIndex: '3',
color: 'white',
textAlign: 'right',
marginRight: '10%'
},
mainNavWrapper: {
paddingTop: '2%',
background: 'rgba(0,0,0,0.8)'
},
mainNavWrapper.sticky: {
I know this does not work. Is it possible?
},
I tried to just string together two classes in MaterialUI and I get errors.
I found the answer. I had to import the react-jss package and follow their documentation. I can now use the jssNested syntax and access nested elements and write rules that only apply if there are two classes attached to the element.
here it is:
import React from 'react'
import { render } from 'react-dom'
// Import React-JSS
import injectSheet from 'react-jss'
// Create your Styles. Remember, since React-JSS uses the default preset,
// most plugins are available without further configuration needed.
const styles = {
mainNav: {
zIndex: '3',
color: 'white',
textAlign: 'right',
marginRight: '10%',
'& ul': {
zIndex: '2',
textAlign: 'right',
listStyleType: 'none',
margin: '0px',
}
},
li: {
'& a': {
color: 'white'
}
},
mainNavWrapper: {
paddingTop: '2%',
background: 'rgba(0,0,0,0.8)',
width: '100%',
opacity: '1',
transition: 'width 2s ease',
padding: '10px',
'&.sticky': {
// jss-nested applies this to a child span
fontWeight: 'bold' // jss-camel-case turns this into 'font-weight'
},
'&.scrolling': {
opacity: '0',
position: 'absolute',
transition: 'opacity 1s ease'
}
},
myLabel: {
fontStyle: 'italic'
}
}
// Define the component using these styles and pass it the 'classes' prop.
// Use this to assign scoped class names.
const Button = ({ classes, children }) => (
<div className={classes.mainNavWrapper}>
<nav className={classes.mainNav}>
<ul className={classes.ul}>
<li className={classes.li} ><a href="#" >home</a></li>
<li className={classes.li}>about us</li>
<li className={classes.li}>packages</li>
<li className={classes.li}>reviews</li>
<li className={classes.li}><a href="#" className={classes.current}>contact us</a></li>
</ul>
</nav>
</div>
)
// Finally, inject the stylesheet into the component.
const Test = injectSheet(styles)(Button)
export default Test;
I think I may have found it (extensive rubberducking)
https://github.com/cssinjs/jss-nested
const styles = {
container: {
padding: 20,
'&:hover': {
background: 'blue'
},
// Add a global .clear class to the container.
'&.clear': {
clear: 'both'
},
// Reference a global .button scoped to the container.
'& .button': {
background: 'red'
},
// Use multiple container refs in one selector
'&.selected, &.active': {
border: '1px solid red'
}
}
}
complies to:
.container-3775999496 {
padding: 20px;
}
.container-3775999496:hover {
background: blue;
}
.container-3775999496.clear {
clear: both;
}
.container-3775999496 .button {
background: red;
}
.container-3775999496.selected, .container-3775999496.active {
border: 1px solid red;
}
Some of my other code is broken so it will take some time to verify this.
I left the other answer because it shows how to solve this issue using react-jss. This you can do the same thing with makeStyles in MaterialUI. I must have had syntax error somewhere which was causing none of my css rules to go into effect.
Here's the makeStyles way, there's also some breakpoint code here to boot:
import { makeStyles } from '#material-ui/core/styles';
const Header = () => {
const useStyles = makeStyles(theme => ({
root: {
padding: theme.spacing(1),
[theme.breakpoints.down('sm')]: {
backgroundColor: 'red',
},
[theme.breakpoints.up('md')]: {
backgroundColor: 'blue',
},
[theme.breakpoints.up('lg')]: {
backgroundColor: 'green',
},
},
mainNav: {
zIndex: '3',
color: 'white',
textAlign: 'right',
marginRight: '10%',
'& ul': {
zIndex: '2',
textAlign: 'right',
listStyleType: 'none',
margin: '0px',
}
},
li: {
display: 'inline-block',
marginLeft: '3%',
'& a': {
color: 'white',
textDecoration: 'none',
marginRight: '10px',
padding: '10px',
'&:hover': {
background: '#3498db'
}
}
},
mainNavWrapper: {
background: 'rgba(0,0,0,1)',
width: '100%',
opacity: '1',
transition: 'width 2s ease',
padding: '10px',
position: 'fixed',
zIndex: 1,
'&.sticky': {
position: 'fixed',
top: '0px',
opacity: '1',
transition: 'opacity 2s ease',
padding: '10px',
zIndex: 1
},
'&.scrolling': {
opacity: '0',
position: 'fixed',
transition: 'opacity 30ms ease'
}
},
...
in the functional component's return ():
<div className={classes.root}>
<div className={classes.mainNavWrapper}>
<nav className={classes.mainNav}>
<ul className={classes.ul}>
<li className={classes.li}><a href="#" >home</a></li>
<li className={classes.li}>about us</li>
<li className={classes.li}>packages</li>
<li className={classes.li}>reviews</li>
<li className={classes.li}><a href="#" className={classes.current}>contact us</a></li>
</ul>
</nav>
</div>
</div>

Categories

Resources