I am using react and styled-components.
When you press the group menu, box1 and box2 will be displayed.
When box1 and box2 are not displayed, there is a blank space between group and user, and I want to remove the blank space between group and user when box1 and box2 are not displayed.
What I've tried
I use dropVisible(state) to control whether box1 and box2 are visible or invisible.
I added dispaly:none when dropVisible is true, and dispaly:block when dropVisible is false, and the animation disappeared. I would like to implement the animation in a working way.
import "./styles.css";
import styled from "styled-components";
import React, { useState, useCallback } from "react";
import { ArrowDown } from "./ArrowDown";
const Item = styled.div`
height: 40px;
width: 300px;
padding: 0px 30px;
text-align: initial;
font-size: 14px;
font-weight: bold;
color: #899098;
&:hover {
background: #fafbfb;
}
`;
const DropMenu = styled.div`
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
height: 100%;
gap: 12px;
:hover {
color: gray;
}
div {
display: flex;
align-items: center;
gap: 12px;
}
`;
const DropText = styled.div`
padding-left: 32px;
`;
const DropMenuWrapper = styled.div<{ visible: boolean }>`
transition: all 0.2s ease;
opacity: ${(props) => (props.visible ? 1 : 0)};
`;
export const App = () => {
const [dropVisible, setDropVisible] = useState(false);
const handleDropVisibleChange = useCallback(() => {
setDropVisible((prevDropVisible) => !prevDropVisible);
}, [dropVisible]);
return (
<div className="App">
<Item onClick={handleDropVisibleChange}>
<DropMenu>
<div>
<span>Group</span>
</div>
<span
style={{
transition: "all 0.2s ease",
transform: `rotate(${dropVisible ? 0 : "0.5turn"})`
}}
>
<ArrowDown />
</span>
</DropMenu>
</Item>
<DropMenuWrapper visible={dropVisible}>
<Item>
<DropMenu>
<DropText>box1</DropText>
</DropMenu>
</Item>
<Item>
<DropMenu>
<DropText>box2</DropText>
</DropMenu>
</Item>
</DropMenuWrapper>
<Item>home</Item>
<Item>user</Item>
</div>
);
};
export default App;
CodePudding user response:
I believe your problem is that, whether the box is opaque or not, it is still taking up space. If you shrink the box, by changing the height in accordance with the visible property, then it will do what you want. You can also change the visibility to hidden.
const DropMenuWrapper = styled.div<{ visible: boolean }>`
transition: all 0.2s ease;
opacity: ${(props) => (props.visible ? 1 : 0)};
height: ${(props) => (props.visible ? "100px" : "0px")};
visibility: ${(props) => (props.visible ? "visible": "hidden")}
`;
CodePudding user response:
@inteoryx is right about your dropdown is taking up space the whole time, so that is why your content keeps that white space.
However, transitioning with height
can be tricky. While it works for this basic application of 2 dropdown items, you will have to update the height on the dropdown component every time you add or remove an item from the list.
Transitions will not work for height
and width
of the value auto
, which is the default state of the html in the browser. This is how browsers calculate the exact size your elements need to be. It will work while transitioning from 2 fixed values, whether px
, rem
, or whatever.
If you open to changing your layout just a little, I have a suggestion that will work for a dropdown menu of dynamic items:
const DropMenuWrapper = styled.div<{ visible: boolean }>`
transition: all 0.2s ease;
transform: ${(props) => (props.visible ? "" : "scaleY(0)")};
transform-origin: top;
position: absolute;
background: white;
box-shadow: 2px 2px 4px rgba(0,0,0,0.4);
`;
This changes the dropdown to be out of the flow, and it will never push any other items out of the way. This is a fairly standard behavior for dropdowns across the web, so you would have to make a conscious style decision to not have this behavior, but that is your call.
In addition to those style changes, I also added a margin-bottom
to your Item
component to make more space now that the Items are right next to the dropdown.