Material UI Button Hover Not Working Absolute Position - css

I have a div with a button and another div in it. The button is normally hidden and the inner div has a bunch of graphs and text. In certain circumstances, I want to blur the inner div and have the button float on top in the middle of the blurred out section, kind of like you see on medium or news sites when asking for subscriptions (although I removed the logic for the example). The way I'm doing it is using absolute positioning for the button, but when I do that, all of the hover functionality just flat out isn't working on the button. It doesn't change the background color of the button or change the cursorI'm using material UI and react. Here is a code sample ->
const useStyles = makeStyles((theme) => ({
blur: {
filter: "blur(7px)",
},
relativePos: {
position: "relative",
},
absolutePos: {
position: "absolute",
top: "50%",
left: "50%",
},
floatingBtn: {
"&:hover": {
cursor: "pointer",
backgroundColor: "red",
},
},
});
// some other stuff
<div className={classes.relativePos}>
<Button
variant="contained"
color="primary"
className={`${classes.absolutePos} ${classes.floatingBtn}`}
>
Button Text
</Button>
<div className={classes.blur}>
{/* Blurred Inner Div Stuff */}
</div>
</div>
I'd love suggestions on either 1) how to get this implementation working OR 2) a better implementation NOT using absolute positioning, if there's a better, more modern approach.

There are two solutions:
use zIndex on the button that is greater than the blurred inner div
Move the button under your blurred inner div
I would prefer the 2nd approach as you don't need to know the zIndex of other elements
const useStyles = makeStyles((theme) => ({
blur: {
filter: "blur(7px)"
},
relativePos: {
position: "relative"
},
absolutePos: {
position: "absolute",
top: "50%",
left: "50%"
// zIndex: 1000 <- Add The zIndex here if you want 1st approach
},
floatingBtn: {
"&:hover": {
cursor: "pointer",
backgroundColor: "red"
}
},
// This is temp button to just toggle the absolute button
tempButton: { margin: "30px 0" }
}));
export default function App() {
const classes = useStyles();
const [showButton, setShowButton] = useState(false);
return (
<div className={classes.relativePos}>
{/* Your graphs area */}
<div className={classes.blur}>This is graphs area</div>
{/* Your absolute button with hover effect */}
{/* you can add it at the bottom and then no need to use zIndex */}
{showButton && (
<button className={`${classes.absolutePos} ${classes.floatingBtn}`}>
Button
</button>
)}
{/* Temp button to show/hide the absolute button, you should have ur own logic */}
<button
className={classes.tempButton}
onClick={() => setShowButton(!showButton)}
>
Click me to show/Hide the button
</button>
</div>
);
}
working example: codesandbox
BTW If you remove filter: "blur(7px)" from the blur class then the hover should work without changing anything in your code. I have no idea why (-_-)

Related

React animation inline styling

I am practicing React inline animation styling. I have made one toggle button, when user press button first time, I want pop up animated card from left to right. when user press button 2nd time it will close the card from right to left. I want to learn how the animation work inline react styling. Unfortunately I am unable to do that. Seems like React inline styling, transitions and translate does not work to me. This is the animation I want to do it. I shared my code in code-sandbox.
This is my code
import { useState } from "react";
export default function App() {
const [toggle, setToggle] = useState(false);
return (
<>
<button onClick={(): void => setToggle(!toggle)}>toogle button</button>
{toggle && (
<div
style={{
display: "flex",
zIndex: 1,
marginLeft: 170,
background: "red",
width: 200,
height: 300,
opacity: 1,
backgroundColor: "tomato",
transition: "opacity 5s"
}}
>
<p style={{ margin: "0px" }}>animation</p>
</div>
)}
</>
);
}
I don't think that this is a good idea, but here is one solution.
You can control everything.
import "./styles.css";
import { useState } from "react";
export default function App() {
const transitions = ["linear", "ease", "ease-in", "ease-out", "ease-in-out"];
const [opacity, setOpacity] = useState(0);
const [right, setRight] = useState(40);
const speed = 0.5;
return (
<>
<button
onClick={() => {
setOpacity(opacity ? 0 : 1);
setRight(prev => prev === 40 ? 20 : 40);
}}
>
toogle button
</button>
<div
style={{
display: "flex",
zIndex: 1,
marginLeft: 170,
background: "red",
width: 200,
height: 300,
opacity,
backgroundColor: "tomato",
transition: `all ${transitions[1]} ${speed}s`,
transform: `translateX(-${right}%)`
}}
>
<p style={{ margin: "0px" }}>animation</p>
</div>
)
</>
);
}
You could use the inlined styles, but you cannot achieve the desired behavior without the use of CSS or a third party library doing the animations for you.
I would recommend to check out this: https://www.w3schools.com/css/css3_animations.asp
Another problem that I see:
You are displaying the content only as soon as the "toggle" property is true, but for animations you need to have different states in your markup in order to transition to different states of animation.
E.g.
<div className="opening">
<div className="opened">
<div className="closing">
<div className="closed"> (or removed from DOM)
Then you can apply the CSS #keyframes to all different stages using the corresponding CSS selectors.
Or if you don't want to dig into CSS yourself. You can use e.g. this library to do the job: https://react-spring.dev/

material UI popover - position on small screen

I have been trying for some time to figure out a way to position material ui popover under my anchor, and leave it like that always even on smaller screens.
Here is a sandbox example: https://codesandbox.io/s/material-demo-yvcqu?file=/demo.js
This is the best I got, but the scroll is not really on the body at this point its on the popover container div, and that does not help me.
Just to explain I know I can use AnchorElement with position but on smaller screens, the popover will just hide the Anchor, I would like the popover to always be under it, and just make the body scroll, so I can see the full popover content when i scroll down.
import React from "react";
import {makeStyles,MuiThemeProvider,createMuiTheme} from "#material-ui/core/styles";
import Popover from "#material-ui/core/Popover";
import Button from "#material-ui/core/Button";
export default function SimplePopover() {
const [anchorEl, setAnchorEl] = React.useState(null);
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const theme2 = createMuiTheme({
overrides: {
MuiButton: {
root: {
top: 400
}
},
MuiPopover: {
root: {
},
paper: {
height: 500
}
}
}
});
return (
<div>
<MuiThemeProvider theme={theme2}>
<Button
variant="contained"
color="primary"
onClick={handleClick}
>
Open Popover with anchor
</Button>
<Popover
id="popover-with-anchor"
open={Boolean(anchorEl)}
anchorEl={anchorEl}
onClose={handleClose}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'center',
}}
transformOrigin={{
vertical: 'top',
horizontal: 'center',
}}
>
Popover content.
</Popover>
</MuiThemeProvider>
</div>
);
}
Images for example. When popover is bigger than the screen it fits itself in the screen and go overs the anchor
instead of being under the anchor
It's interesting the Popover component doesn't have a property to handle this situation. I ran into a similar issue on a smaller device when I had a long list of data in the popover. To fix this, I just set top myself on the PaperProps property of the Popover component. See below:
<Popover PaperProps={{ style: { top: myAnchor.current ? myAnchor.current.getBoundingClientRect().bottom : 0 } }}></Popover>
Don't do overflow: scroll on root. Instead, do overflowY: auto on paper.
See codesandbox and play around.
Try this:
overrides: {
MuiPopover: {
root: {
// overflow: "scroll"
},
paper: {
left: 50,
top: "500px !important",
height: 50,
overflowY: "auto"
}
}
}

Change background-color of an unchecked Material-UI checkbox

I am working with React-Redux and Material-UI for styling. I have a < Checkbox /> that is on a toolbar. The toolbar's background-color is blue (#295ED9).
I've managed to change the color of the checkbox, when it is checked. I have also changed the color of the outline of the checkbox to white, when it is unchecked.
Problem: I cannot change the background-color / fill-color of the checkbox when it is unchecked. It is always the same color as the toolbar it is on (blue - #295ED9).
I have tried changing the background-color, color, and fill attributes of the < svg > and the < path > to white.
The best result I can get is when I change the < svg > fill attribute to white. Unfortunately, this does not make the checkbox fill with a white background when unchecked.
JSX
<div className="hasBalance">
<FormControlLabel
control={<Checkbox color="primary"/>}
label="Has Balance"
/>
</div>
SCSS
.hasBalance {
grid-area: balance;
.MuiSvgIcon-root {
fill: white;
}
}
Elements in Chrome Inspect Tool
<div class="hasBalance">
<label class="MuiFormControlLabel-root">
<span class="MuiButtonBase-root MuiIconButton-root PrivateSwitchBase-root-143 MuiCheckbox-root MuiCheckbox-colorPrimary MuiIconButton-colorPrimary" aria-disabled="false">
<span class="MuiIconButton-label">
<input class="PrivateSwitchBase-input-146" type="checkbox" data-indeterminate="false" value="">
<svg class="MuiSvgIcon-root" focusable="false" viewBox="0 0 24 24" aria-hidden="true" role="presentation">
<path d="M19 5v14H5V5h14m0-2H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z"></path>
</svg>
</span>
<span class="MuiTouchRipple-root"></span>
</span>
<span class="MuiTypography-root MuiFormControlLabel-label MuiTypography-body1">Has Balance</span>
</label>
</div>
The goal is to change the background-color / fill-color of the unchecked Material-UI checkbox to white. Thank you.
Below is an approach that seems to work. This uses the ":after" pseudo-element to place a white background behind the SVG.
import React from "react";
import ReactDOM from "react-dom";
import { withStyles } from "#material-ui/core/styles";
import Checkbox from "#material-ui/core/Checkbox";
const WhiteBackgroundCheckbox = withStyles(theme => ({
root: {
color: "red",
"& .MuiIconButton-label": {
position: "relative",
zIndex: 0
},
"&:not($checked) .MuiIconButton-label:after": {
content: '""',
left: 4,
top: 4,
height: 15,
width: 15,
position: "absolute",
backgroundColor: "white",
zIndex: -1
}
},
checked: {}
}))(Checkbox);
function App() {
return (
<div style={{ backgroundColor: "blue" }}>
<WhiteBackgroundCheckbox />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Related question: Change the tick color in MuiCheckbox material UI
MUIv5
Accepted answer doesn't work in MUIv5. MuiIconButton-label doesn't exist in Checkbox anymore and it changed the structure of the component.
For anyone struggling with v5, here's my kinda-hacky solution :
import { Components } from '#mui/material'
const MuiCheckbox: Components['MuiCheckbox'] = {
styleOverrides: {
root: {
'& .MuiSvgIcon-root': {
zIndex: 1,
},
'& .PrivateSwitchBase-input': {
width: 'auto',
height: 'auto',
top: 'auto',
left: 'auto',
opacity: '1',
visibility: 'hidden', // optional
'&::before': {
content: '""',
position: 'absolute',
background: 'white',
height: "100%",
width: "100%",
visibility: "visible" // optional
},
}
},
},
}
It relies on the fact that the <input> element assumes just the right dimensions and position if we reset its size and positioning attributes. That way the solution is resistant to various paddings and size props applied to the root element of the component. AFAIK it shouldn't break anything, but my experience with MUI is not that long.
Note that I have this solution defined as a global styleOverride in my theme's config and I want this custom background to affect checked tick too - mix and match with Ryan's answer for your scenario.
Visibility attributes are optional, they just make sure the input element stays hidden (I set opacity 1 on it, default is 0). Just make sure you remove both if you don't want them.

Set background of div using flex

i am trying to develop a movie portal using react js. I am trying to display the movie poster, and the movie details adjacent to it.
I am using flex, but am encountering an issue with the output. The movie details are being pushed to an extreme end, and i am unable to figure out the reason.
I am trying to get the "The Dark Knight" adjacent to the poster.
This is the output screenshot
my Js code is as follows,
dialogContent: (backgroundUrl) => ({
backgroundImage: `linear-gradient(rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0.6)), url(${backgroundUrl}) `,
backgroundSize: 'cover',
overflow: 'hidden',
backgroundRepeat: 'no-repeat',
backgroundSize: '100%',
height: '33%',
minHeight: 400,
color: 'white',
padding: 10
}),
poster: () => ({
padding:5,
width:'100%',
margin:"auto"
}),
text:() =>({
color:"white"
}),
main:() =>({
backgroundColor: "black",
display: "flex"
})
}
class MovieDisplay extends Component {
render() {
//console.log(this.props);
const {movie} = this.props.location.state.movie;
return (
<div style={styles.main()}>
< div style={styles.poster()}>
<img src={movie.poster_path} />
</div>
<div style={styles.dialogContent(movie.backdrop_path)}>
<h1 style={styles.text()}>{movie.original_title}</h1>
<h2 style={styles.text()}>{movie.popularity}</h2>
</div>
<div >
<h5 style={styles.text()}> more content would be updated soon</h5>
</div>
</div>
);
}
}
Can anyone help figure out the reason?
Thanks in advance.
The problem is with the poster style set to width of 100% and margin of 'auto'.
poster: () => ({
padding:5,
// width:'100%', <-- Try changing this by either removing it or setting to 'auto' or a smaller value
// margin:"auto" <-- Get rid of this
})
One solution would be to add another inside main container, this way you have better control over the poster and movie information spacing. You should also add some justification/alignment to your flex styling, otherwise the child elements will float to the ends of the flexed container (As seen in your example).
Add the below to your styles.
main:() =>({
backgroundColor: "black",
alignItems: 'center'
}),
/*add this style*/
innerMain: () =>({
display: "flex",
}),
And then in your MovieDisplay component, write this.
<div style={styles.main()}>
<div style={styles.innerMain()}>
...
<div style={{width: 100 + '%'}}>
<h5 style={styles.text()}> more content would be updated
soon
</h5>
</div>
</div>
<div>
Edit: I've added a codepen to demonstrate what these styles will accomplish. More styling (like for the poster image) might be required but this is the general layout of what you're looking for.
I also added a minWidth of 200 to the dialogContent and made the div containing more content... have a width of 100% to fill the remaining space.

Have a "sticky" button right underneath another "sticky" navbar

I have an app file which contains my own custom appbar and different page components:
const styles = theme => ({
appBar: {
width:'100%',
},
});
class App extends Component {
render() {
const {classes} = this.props;
return (
<React.Fragment>
<CssBaseline />
<AppBar position="sticky" className={classes.appBar} />
<Page1 show={someCondition} />
<Page2 show={someCondition} />
.
.
<Page99 show={someCondition} />
</React.Fragment>
);
}
}
The Appbar is sticky so it always shows on the top.
Each page component has a button which is always on the top of that page:
const styles = theme => ({
button: {
width:'100%',
},
});
class Page99 extends Component {
render() {
const {classes} = this.props;
return (
<React.Fragment>
<div>
<Button variant="contained" className= {classes.button}>
Action Button
</Button>
</div>
{/* Some other stuff */>
</React.Fragment>
);
}
}
I know want this button to always be right under the appbar. So when the user scrolls down this button should remain sticky just like the appbar does. I tried to set the positioning to sticky hoping it would stack underneath it but it wouldn't. The appbar is dynamic so I don't know the exact height it will be since on different resolutions it will look different so I couldn't use something like fixed positioning.
You can set position of page container as relative and set button as absolute.
the you can align it to top right of the page or wherever you want.
Check this fiddle is this is what you need
.componentparent {
position: relative;
height:100px;
max-height: 50px;
overflow: auto;
}
.button {
position: fixed;
top: 30px;
}
.otherelements{
top: 70px;
position: relative;
}
<div id="parent-container">
<div> your app bar </div>
<div class='componentparent'>
<button class='button'>my button</button>
<div class='otherelements'>your component</div>
</div>
</div>
Place your button inside your appbar and set your button to position to absolute and add top: 100% to move it exactly at the bottom of appbar.

Resources