Add hover effect to react div using inline styling - javascript

I have a div which takes the shape of a circle the css property to display a circle is taken from the circle class. The color of the circular div is taken from the inline styling. Here a function called Status() is used where it will return a hex color code. The circle renders with the colors according to the status we pass to the Status function. To achieve the hover effect i added a ':hover' property to the styling object but it doesn't work. Here is the code that i have tried. Any idea on how to achieve this?. i need to add a boarder/glow to the circle on mouse hover.
<div
className="circle"
style={{
backgroundColor: Status('new'),
':hover': {
boxShadow: `0px 0px 4px 2px ${Status('complience')}`,
},
}}
/>

Try adding & before :hover
This is not possible with inline styles, you may want to use onMouseEnter and onMouseLeave props to get the hover state and use it, for example :
class MyComponent extends React.Component {
state= {
hover: false,
}
handleMouseEnter = () => {
this.setState({ hover: true });
}
handleMouseLeave = () => {
this.setState({ hover: false });
}
render() {
const { hover } = this.state;
return(
<div
className="circle"
style={{
backgroundColor: Status('new'),
...(hover && { boxShadow: `0px 0px 4px 2px ${Status('complience')}`}),
}}
onMouseEnter={this.handleMouseEnter} // Or onMouseOver
onMouseLeave={this.handleMouseLeave}
/>
)
}
}
Alternatives :
Use a third party styling library (e.g. Styled-components)
Use classnames / css stylesheets

Related

How can I style a toastify toast in the same js file, without using the styled toastcontainer?

in my app I use the ToastContainer component to display more than one type of toast. I do not want them both to have the same style, therefore I cannot use a styled ToastContainer to achieve what I want.
I know from the documentation for toastify that a toast can have it's own className for which you can specify the color etc. in a css file. But I want to have the css in the javascript file. But I have not yet found a way for it to work.
This is what I have come up with:
const notify = () => {
toast(customMsg, {
className: container,
autoClose: false,
closeOnClick: false,
position: toast.POSITION.BOTTOM_RIGHT,
});
};
const container = {
backgroundColor: 'yellow !important',
color: ' black !important',
borderRadius: '10px !important',
border: '2px solid orange',
width: '200px'
};
This does not work, but if I change className to "container" and put the styling in a css file it works. Is there a way for the className to be specified in the javascript file?

How to custom color text and icon in TableSortText component of Material-ui?

What I'm trying to do:
I am trying to provide the user the option to provide custom styling to my EnhancedTable component by passing in a styles object containing properties such as headCellColor, headCellBackgroundColor, bodyCellColor, bodyCellBackgroundColor etc which can be used to color the cells in TableHead and TableBody.
In the TableHead component, I use a TableSortLabel in a way similar to what they've done in this material-ui docs example: https://material-ui.com/components/tables/#sorting-amp-selecting
I wish to custom color the text and the arrow icons on hover and when active based on the props provided by the user.
Let's see the colors of TableSortLabel in different situations:
The color of the text is grey initially and there is no arrow. When mouse is hovered over it, a grey arrow appears and the text turns black. On clicking it, active state is set, the grey arrow turns black and the text turns black permanently until active state is removed.
What I've tried so far:
const useStyles = makeStyles({
tableSortLabel: props => ({
backgroundColor: "blue",
color: props.headCellColor,
fill: props.headCellColor,
"&:hover": {
backgroundColor: "blue"
}
})
});
function EnhancedTableHeadCell(props) {
const { isActive, onHoverSortState, clickHandler, ...otherProps } = props;
const classes = useStyles(props.styles);
return (
<FancyTableCell styles={props.styles} {...otherProps}>
<TableSortLabel
active={isActive}
classes={{
icon: classes.tableSortLabel,
active: classes.tableSortLabel
}}
direction={onHoverSortState}
onClick={clickHandler}
>
{props.children}
</TableSortLabel>
</FancyTableCell>
);
}
This is what it looks like in the browser:
https://i.postimg.cc/fW7W2MRB/c1.jpg
The first one is a normal header, the second is on hover and the third is when clicked (active state).
From what we can observe, the text color is totally unaffected by the color css property in all the three cases (normal, hover, active). On hover, backgroundColor only affects the icon and not the text. However, we can see that backgroundColor affects the text when it is active. Everything is going as expected with the icon. The only issue is with the text.
What could I be possible doing wrong? How can I solve my problem?
What worked for me is:
const StyledTableSortLabel = withStyles((theme: Theme) =>
createStyles({
root: {
color: 'white',
"&:hover": {
color: 'white',
},
'&$active': {
color: 'white',
},
},
active: {},
icon: {
color: 'inherit !important'
},
})
)(TableSortLabel);
You can reference the following for increasing css specificity:
https://material-ui.com/customization/components/#pseudo-classes
Solution for your problem is following:
MuiTableSortLabel: {
root: {
color: textPrimary,
// if you want to have icons visible permanently
// '& $icon': {
// opacity: 1,
// color: primaryMain
// },
"&:hover": {
color: primaryMain,
'&& $icon': {
opacity: 1,
color: primaryMain
},
},
"&$active": {
color: primaryMain,
// && instead of & is a workaround for https://github.com/cssinjs/jss/issues/1045
'&& $icon': {
opacity: 1,
color: primaryMain
},
},
},
}
This restyling I use globally via my ThemeProvider, but you can of course use it individually in your single component by using "withStyles" HOC (see "BootstrapButton" in example)
I could'nt find a proper way to do it so I came up with a temporary solution overriding the material ui css.
I added this to my global css:
.MuiTableSortLabel-root.MuiTableSortLabel-active,
.MuiTableSortLabel-root:hover,
.MuiTableSortLabel-icon {
color: inherit !important;
}
Worked for me with Mui5:
sx = {
{
'&.MuiTableSortLabel-root': {
color: 'white',
},
'&.MuiTableSortLabel-root:hover': {
color: 'blue',
},
'&.Mui-active': {
color: 'blue',
},
'& .MuiTableSortLabel-icon': {
color: 'blue !important',
},
}
}
'&.MuiTableSortLabel-root' <-- no space &.
'&.Mui-active' <-- no space &.
'& .MuiTableSortLabel-icon' <-- space

How do I change the ripple background color on Button?

So far in the API (v3.9.2), I see a mention of TouchRippleProps for ButtonBase for https://material-ui.com/api/button-base/
My button looks like
<Button variant="text"
size={"large"}
fullWidth
className={classes.button}
>
{value}
</Button>
and my button style is .
button: {
backgroundColor: colors.surface,
borderRadius: 0, // to make buttons sharp edged
touchRipple: colors.primary
}
When I touch a button, I see a white background (see number 5) as
My question is that When I touch a button, how can I change that background from white to let's say blue and then let it fade away?
UPDATE .
I achieved reasonable behavior with the following changes to your numberPadStyle:
const numberPadStyle = theme => ({
button: {
backgroundColor: colors.surface,
borderRadius: 0, // to make buttons sharp edged
"&:hover": {
backgroundColor: colors.primary,
// Reset on touch devices, it doesn't add specificity
"#media (hover: none)": {
backgroundColor: colors.surface,
"&:active": {
backgroundColor: colors.primary
}
}
},
"&:active": {
backgroundColor: colors.primary
}
}
});
The issue with touch screens is that a touch triggers the "hover" effect and it doesn't go away till you touch somewhere else. "#media (hover: none)" targets the hover effect for touch devices (https://developer.mozilla.org/en-US/docs/Web/CSS/#media/hover). The "active" CSS is in effect during the touch/click and then the ripple built in to Button takes care of the rest.
You can, of course, adjust the hover and active colors as desired.
It doesn't appear animations other than the ripple are supported. However, you can create something like this TriggeredAnimation wrapper component:
class TriggeredAnimationWrapper extends Component {
constructor(...args) {
super(...args)
this.state = {
wasClicked: false
}
this.triggerAnimation = this.triggerAnimation.bind(this)
}
triggerAnimation(callback) {
return (...args) => {
this.setState(
{wasClicked: true},
() => requestAnimationFrame(()=>this.setState({wasClicked: false}))
)
if (callback) return callback(...args)
}
}
render() {
const {
triggerAnimation,
props: {
children
},
state: {
wasClicked
}
} = this.props
return children({wasClicked, triggerAnimation})
}
}
And use it like this:
<TriggeredAnimationWrapper>({wasClicked, triggerAnimation}) => (
<Button
onClick={triggerAnimation((e) => console.log('clicked', e))}
className={wasClicked ? 'clicked' : ''}
/>
)</TriggeredAnimationWrapper>
Then, you can create a css animation and change the background when the click class is present.

How to animate image for 5 seconds in React using CSS?

I'm using React with Redux, and I have the following situation. In my component I have a div that holds and image, and the component is also receiving a property from my Redux state which is called showIcon. So, if showIcon is not null, I want the image to be displayed for 5 seconds, and once the 5 seconds passes, I want it to disappear and set the showIcon value to null by dispatching an action that I have like updateShowIcon(null);. How can I do this properly in React, and how can I use CSS to show and animate the icon as I want?
import React, { Component } from 'react';
class MyComp extends Component {
render() {
return (
<div style={styles.mainDiv}>
<div style={styles.childDiv}>
{
this.props.showIcon &&
<div style={styles.iconStlyes}>
<img src={process.env.PUBLIC_URL + '/icons/myicon.png'}/>
</div>
}
// partially removed for brevity, some other component
</div>
</div>
);
}
}
const styles = {
iconStlyes: {
position: 'absolute',
zIndex: 10,
},
mainDiv: {
overflow: 'auto',
margin: 'auto',
height: 'calc(100vh - 64px)',
padding: 0,
},
childDiv: {
height: 'calc(100vh - 64px)',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
},
};
export default MyComp;
Whenever I detect a change in componentWillReceiveProps I would create a timer and dispatch the action. Remember to clear the timeout on componentWillUnmount.
The idea is based on you showing and hiding the icon with css and not with react conditional rendering, so once you need to show the icon you add the class show or remove it once you don't need to show it.
It would probably look like this:
componentWillReceiveProps(nextProps){
if (nextProps.showIcon && nextProps.showIcon !== this.props.showIcon){
this.timer = setTimeout(() => {nextProps.updateShowIcon(null)}, 5000);
}
}
componentWillUnmount(){
clearTimeout(this.timer);
}
render() {
const {showIcon} = this.props;
return (
<div style={styles.mainDiv}>
<div style={styles.childDiv}>
<div style={styles.iconStlyes} className={`${showIcon ? 'show':''} icon-container`}>
<img src={process.env.PUBLIC_URL + '/icons/myicon.png'}/>
</div>
</div>
</div>
);
}
and your css for a simple fade animation:
.icon-container{
opacity: 0;
transition: opacity: 500ms ease-in;
}
.icon-container.show{
opacity: 1;
}
If it is important for you to use the store state then you can manage the showIcon property via componentWillReceiveProps(nextProps) and do something like:
componentWillReceiveProps(nextProps){
if(!this.props.showIcon && nextProps.showIcon){
setTimeout(()=>dispatch(updateShowIcon(null)),5*1000);
}
//manage clear timeout if necessary
}
But for the animation part its better to use the showIcon property as a class and not for adding/removing it from the DOM, like:
<div style={styles.iconStlyes} className={this.props.showIcon?'show':'hide'}>
<img src={process.env.PUBLIC_URL + '/icons/myicon.png'}/>
</div>
and the styles should manage it:
iconStyles: {
position: 'absolute',
zIndex: 10;
transition: //effects of specified or all attributes
&.show{
visibility: visible;//display:block
}
&.hide{
visibility: hidden;//display:none
}
}

React - change state right after previous state change was rendered

I wanna make cool box grow animation (expand) when user clicks on it and I want to do it following way:
user clicks on expand button -> get div dimensions and top/left positions via ref, store it in state and assign div's style to these values
changed expanded state variable and change div's position to fixed, also change left, top values and width, height css values
My problem is in initial div expand click. It seems that both state's changes are rendered in one cycle so I don't see smooth animation on first expand click. I've tried to do it via setState callback, also tried to update expanded in componentDidUpdate method once div dimensions are in state, nothing worked except delaying expanded set via setTimeout.
Code example via setState callbacks
if (chartsExpanded.get(chart) === "collapsed-end" || !chartsExpanded.get(chart)) {
this.setState({
chartsProportions: chartsProportions.set(
chart,
Map({
left: chartProportions.left,
top: chartProportions.top,
width: chartProportions.width,
height: chartProportions.height
})
)
}, () => {
this.setState({
chartsExpanded: chartsExpanded.set(chart, "expanded")
})
})
}
...
<div
className={`box customers-per-sources-count ${
customersPerSourcesCount.loading ? "loading" : ""
} ${
chartsExpanded.get("customersPerSourcesCount")
? chartsExpanded.get("customersPerSourcesCount")
: "collapsed-end"
}`}
ref={el => {
this.chartRefs["customersPerSourcesCount"] = el
}}
style={{
left: chartsProportions.getIn(["customersPerSourcesCount", "left"], "auto"),
top: chartsProportions.getIn(["customersPerSourcesCount", "top"], "auto"),
width: chartsProportions.getIn(["customersPerSourcesCount", "width"], "100%"),
height: chartsProportions.getIn(["customersPerSourcesCount", "height"], "100%")
}}
>
How can I achieve that style from chartsProportions will be rendered before class based on expanded value is changed? I don't want to use setTimeout nor want to update all charts proportions onScroll event etc.
You just need to pass setState a function instead of an object, to ensure the state changes are applied in order:
this.setState(previousState => ({
previousChange: "value"
}))
this.setState(previousState => ({
afterPreviousChange: previousState.previousChange
}))
https://reactjs.org/docs/react-component.html#setstate
Another option might be to pass a callback to setState that runs after the state changes have been applied, like:
this.setState({ someChange: "value" }, () => this.setState({
otherChange: "value"
}))
CSS transitions could help with this too.
Using React state to animate properties is not the right way to do it. State updates will always get batched and you generally don't want to re-render your entire component 60 times per second
Store 'expanded' boolean in your state, and change element's class accordingly. Use css to add animations between two states
handleClick = () => {
this.setState({ expanded: !this.state.expanded })
}
render() {
return (
<div
className={`box ${this.state.expanded ? 'expanded' : ''}`}
onCLick={this.handleClick}
/>
)
}
in your css
.box {
width: 100px;
height: 100px;
transition: width 2s;
}
.expanded {
width: 300px;
}
added based on comments:
What you want to do is:
set position: fixed to your element. This would snap it to the top of the screen instantly, so you need to pick the right top and left values so that position fixed starts off where it was when position was static (default). For that you can use element.getBoundingClientRect()
calculate desired top and left attributes that would make your element appear in the middle of a screen, and apply them
very important: between step 1 and 2 browser has to render the page to apply position and initial top and left values, in order to have something to start animation from. It won't be able to do that if we apply both of these styles synchronously one after another, as page will not render until JS stack frame is clear. Wrap stage 2 logic in setTimeout which will make sure that browser renders at least once with styles applied at stage 1
rough working example:
class Example extends React.Component {
constructor(props) {
super(props)
this.state = {
expanded: false,
style: {}
}
}
handleClick = (e) => {
if (!this.state.expanded) {
const r = e.target.getBoundingClientRect()
const style = {
top: r.y,
left: r.x,
}
this.setState({
expanded: !this.state.expanded,
style
})
setTimeout(() => {
this.setState({
style: {
top: (window.innerHeight / 2) - 50,
left: (window.innerWidth / 2) - 50,
}
})
})
} else {
this.setState({
expanded: false,
style: {}
})
}
}
render() {
return (
<div className={'container'}>
<div className={'empty'} />
<div className={'empty'} />
<div className={'empty'} />
<div
onClick={this.handleClick}
className={`box ${this.state.expanded ? 'expanded' : ''}`}
style={this.state.style}
/>
</div>
)
}
}
and styles.css
* {
box-sizing: border-box;
padding: 0;
margin: 0;
}
.container {
height: 200vh;
}
.empty {
height: 100px;
width: 100px;
border: 1px solid gray;
}
.box {
height: 100px;
width: 100px;
border: 3px solid red;
transition: all 0.5s;
}
.expanded {
position: fixed;
}

Categories

Resources