How to change other element when hover by using 'styled' - reactjs

I'm new to Mui and trying to apply animation to components.
What I want to do is, I have four same component and each has its own image.
<MyComponent>some images...</MyComponent>
<MyComponent>some images...</MyComponent>
<MyComponent>some images...</MyComponent>
<MyComponent>some images...</MyComponent>
const MyComponent = styled("div")((theme) => ({
//... some styles.
// scale up when hovered
'&:hover': {
transform: "scale(1.2)",
marginRight: "20px",
}
}));
If I hover a <MyComponent>, I want to scale up hovered one, and scale down others.
Is there any ways to defined such action by using styled???

I would set a state then use a conditional to change styles .. IE
const [hoverState, setHoverState] = useState(false);
<MyComponent
onMouseOver={setHoverState(true)}
onMouseOut={setHoverState(false)}
style={hoverState ? {transform: "scale(1.2)",marginRight: "20px",} : ''}
>
some images...
</MyComponent>

You can do this with css and styled-components.
Note that the styled function is not meant to be called directly, but passed a template literal. Inside the template literal, you can write regular css expressions.
codesandbox
const MyComponent = styled.div`
transform: scale(1);
margin-right: 0px;
:hover {
transform: scale(1.2);
margin-right: 20px;
}
`;

Related

CSS transition not working when changing react state

I want to see animation when I change width of my sidebar. Material UI is used here (AppBar). I think it's because React just re-renders component quickly. How you would do it? I think it would work if i just changed class or added class.
Also it' necessary to understand that content of StyledAppBar will depend on whether it's opened. So I will need re-render so that the content change
Additional question - do you know how to make AppBar render as tag instead oh tag? :)
const Header = () => {
const [open, setOpen] = useState(true);
const StyledAppBar = styled(AppBar)`
background-color: red;
height: 100vh;
width: ${open ? '240px' : '50px'};
transition: width 1s ease-in-out;
position: fixed;
left: 0;
`;
return (
<StyledAppBar>
<Toolbar>
<Typography>MUI SHOP</Typography>
<ShoppingBasket onClick={() => setOpen(prev => !prev)}/>
</Toolbar>
</StyledAppBar>
);
};

styled-components — how to use :not() selector on a prop?

Let's say I have an element with a property "selected":
<Button selected>
Text
</Button>
const Button = styled.div`
&:not(???) {
color: red;
}
`;
How do I select all instances of Button which don't have the "selected" property?
Maybe there is another way to do what I'm trying to achieve?
Сlarification edit:
I actually need to use :not together with :hover, like this:
const Button = styled.div`
&:not(???):hover {
color: red;
}
`;
styled-components uses some template magic to allow you to access passed props like so:
const Button = styled.div`
color: ${props => props.selected ? "color when selected" : "red"};
`;
With styled-components, this tends to be the more idiomatic way of setting things based on props rather than using CSS selectors.
That's how you can do it:
const Button = styled.div`
${props => !props.selected && css`
:hover {
color: red;
}`
}
`;
Don't forget to import css from styled-components;

I want the arrow icon to flip up and down every time the state changes.and I want to animate it

I am using react, styled-components.
When state(visible) is set to true, DropMenu box1 and box2 will be displayed.
We want the ArrowDown icon to flip upward when state is true, and downward when false.
I also want to apply an animation when flipping it.
I want to add an animation like the Dropdown in the following site.  
Reference site
code
import "./styles.css";
import styled from "styled-components";
import React, { useState, useCallback } from "react";
import { ArrowDown } from "./ArrowDown";
const Item = styled.div<{ active?: boolean }>`
height: 40px;
width: 300px;
padding: 0px 30px;
&:hover {
background: #fafbfb;
}
`;
const DropMenu = styled.div`
display: flex;
align-items: center;
justify-content: space-between;
color: #899098;
width: 100%;
height: 100%;
font-size: 14px;
font-weight: bold;
gap: 12px;
:hover {
color: gray;
}
div {
display: flex;
align-items: center;
gap: 12px;
}
`;
const DropText = styled.div`
padding-left: 32px;
`;
export const App = () => {
const [visible, setVisible] = useState(false);
const handleDropVisibleChange = useCallback(() => {
setVisible((prevVisible) => !prevVisible);
}, [visible]);
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<Item onClick={handleDropVisibleChange}>
<DropMenu>
<div>
<span>Menu</span>
</div>
<ArrowDown />
</DropMenu>
</Item>
{visible && (
<div style={{ transition: "all 0.5s ease" }}>
<Item>
<DropMenu>
<DropText>box1</DropText>
</DropMenu>
</Item>
<Item>
<DropMenu>
<DropText>box2</DropText>
</DropMenu>
</Item>
</div>
)}
</div>
);
};
export default App;
TLDR
Change your MenuItem component warpper to something like
const DropMenuWrapper = styled.div<{ visible: boolean }>`
transition: all 0.5s ease;
opacity: ${(props) => (props.visible ? 1 : 0)};
`;
replace the visibility switch mechanism with following
- {visible && (
- <div style={{ transition: "all 0.5s ease" }}>
+ <DropMenuWrapper visible={visible}>
similar action can be added to the arrow-down icon also with style
(The ArrowDown SVG icon must accept style if it is custom written component)
<ArrowDown
style={{
transition: "all 0.5s ease",
transform: `rotate(${visible ? 0 : "0.5turn"})`
}}
/>
Why this happened:
When a component (sub-component/element) is mounted in react, it starts a complete life cycle toward browser paint.
So it is must have the property which causes the element to animate, for example, I added the opacity transition to the example itself, forcing it to animate in the first look and in disappearing.
Although it comes with some performance cost of having unseen elements still in the dom (but not visible), making it bad for accessibility too, it is the simplest way to achieve this behavior.
Consider this example If you have an animated element, does it show the animation if you refresh the browser if the answer is yes, it will show animation in react too.
Another way of doing some animation in react.
Using third-party library react-transtion-group which is heavily used in lots of packages e.g. Material-UI.
In this case you can also trigger the end event and start to unmount the component as the animation disappears and end completely.
Using framer motion
If you want to take your understanding of what is needed for the transition when the component is unmounted and removed from aka dom, I highly encourage you to read the animation section of svelte docuementation
What I did, what might look stupid to more advanced developers was implement a simple check that would switch icons.
Note: This doesn't have an animation, though. It's just a simple switcharoo
define state in component
const [isOpen, setIsOpen] = useState(false);
Check whether icon is open or closed, if open, ExpandLessIcon, if closed ExpandMoreIcon.
<ExpandLessIcon
onClick={() => {
setIsOpen(!isOpen);
}}
/>
) : (
<ExpandMoreIcon
onClick={() => {
setIsOpen(!isOpen);
}}
/>
)}
The way it works is, once clicked, it'll just flip the true false state over and over, which in turn will change icons.

React js onclick issue

I am trying to create an custom Radio button like feature. By putting onClick on div to change the state value and I am not able to change the same. Please help me understand what I am missing on.
const [category, setCategory] = useState("");
const handleSubmit = e => {
console.log(e);
setCategory(e.target.value);
};
return (
<>
<CenterAlignedColumnContainer>
<FormHeadingText>
<FormSectionHeadingTextContainer>
Category
</FormSectionHeadingTextContainer>
</FormHeadingText>
<CategoryContainer>
<TextRadioButton
value="Salads"
onClick={handleSubmit}
name="category"
>
<RadioButtonText>
<TextContainer>Salads</TextContainer>
</RadioButtonText>
</TextRadioButton>
<TextRadioButton value="Pasta" onClick={handleSubmit} name="category">
<RadioButtonText>
<TextContainer>Pasta</TextContainer>
</RadioButtonText>
</TextRadioButton>
</CategoryContainer>
<PartialWidthDivider />
</CenterAlignedColumnContainer>
</>
);
}
Styled div for custom Radio button
export const TextRadioButton = styled.div`
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
padding: 10px;
background: rgba(176, 167, 230, 0.5);
box-shadow: 0px 4px 4px rgba(176, 167, 230, 0.5);
border-radius: 20px;
margin-left: -10px;
margin: 8px;
`;
div does not have a value property, so you should reference it i.e. as a passed prop:
<TextRadioButton value='something' onClick={handleSubmit(props.value)} />
and use it explicitly
handleSubmit(value) {
setCategory(value);
}
change your onClick method to :
onClick={(e)=>handleSubmit(e}
or
onClick={
(e)=>{
console.log(e);
setCategory(e.target.value);
};
So there's a couple of problems wrong with this, this first and foremost being that you have a pseudo-element covering your entire display:
export const Background = styled.div`
width: 100%;
&:before {
display: block;
position: absolute;
width: 100%;
height: 400vh;
content: "";
background: url(https://res.cloudinary.com/antilibrary/image/upload/v1595154947/Piatto/backgroundFormNew_sk7yie.svg)
repeat center center;
opacity: 0.5;
}
`;
When an element is :before it means it is above any further elements in the stacking context, the same way that hard-coded elements are. A simple solution is to give the pseudo-element z-index: -1, which will ensure that it is placed behind everything else. Ideally, however, you would not use a pseudo-element at all, but instead a distinct div placed absolutely behind everything.
The second is that your buttons are not using good semantic HTML, and as a result you will not be able to target value, which does not exist on divs. Additionally, since the text of the button is separate from the button itself, there will be cases where it returns undefined anyway as the target of the event does not hold the button's value.
What I suggest you do first is change TextRadioButton to be a styled.button, you may need to adjust some of your styles to accommodate this. Then, change the event handler to the following:
const handleSubmit = e => {
const value = e.currentTarget.value;
setCategory(value);
};
The currentTarget of an event is the element where the event is registered, not whichever child element actually triggered it, which you access with regular target. This way, no matter which part of the button is clicked on, it will always reference the value on the top level of the button component.

Emotion.js how to do composition / conditional styling in react-native?

https://github.com/emotion-js/emotion/tree/master/packages/native
gives example
style={css`
border-radius: 10px;
`}
However, I can't figure out how I can do composition as done in https://emotion.sh/docs/composition with react-native
<div css={[danger, base]}>
Nor I can't do conditional styling as done in https://emotion.sh/docs/styled
const Button = styled.button`
color: ${props =>
props.primary ? 'hotpink' : 'turquoise'};
`
Even if I could do, they use two different methods one using css one using styled . How can I get the two at the same time with react-native?
Question 1: How to do composition in emotion/native in React Native?
It's really simple all you need to is use style property like so:
const style1 = css`
font-size: 40px;
`
const color = css`
color: red;
`
// And then in your component you can pass the all your style objects
// in an array to style property
<Text style={[fontSize, color]}>Hello world</Text>
Question 2: Conditional style in emotion/native in React Native?
const Description = styled.Text`
font-size: ${props => props.fontSize !== undefined ? props.fontSize : "40px"};
color: hotpink;
`
<Description fontSize={"60px"}>
Change code in the editor and watch it change on your phone! Save to get a shareable url.
</Description>
Here's a working exampe as a snack

Resources