Is it possible to animate opening and closing the menu? The menu node is removed or attached to DOM tree on opening/closing so this interferes with css animation. Is there a work around for this?
For opening animation, it's relatively easy, you only have to add the animation to the <Menu/>
component using css.
The following example use fade-in/out animation
@keyframes fadeIn {
0% {
opacity: 0;
transform: translateY(2rem);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
.menu {
animation: fadeIn 0.2s ease-in-out;
}
In your render method
<Select
{...}
components={{
Menu: (props) => <components.Menu {...props} className="menu" />
}}
/>
The closing animation is a bit trickier because when the close
event fires, the menu element is removed immediately, leaving no time for the closing animation to run.
We need to change the close
event behavior a little bit. The strategy is to just let the menu close abruptly as normal. But before that, we make another clone of the menu, and that clone will run the closing animation and remove itself when the animation finished.
// generate unique ID for every Select components
const [uniqueId] = React.useState(
() => 'select_' + Math.random().toFixed(5).slice(2),
);
return (
<Select
id={uniqueId}
onMenuClose={() => {
const menuEl = document.querySelector(`#${uniqueId} .menu`);
const containerEl = menuEl?.parentElement;
const clonedMenuEl = menuEl?.cloneNode(true);
if (!clonedMenuEl) return; // safeguard
clonedMenuEl.classList.add("menu--close");
clonedMenuEl.addEventListener("animationend", () => {
containerEl?.removeChild(clonedMenuEl);
});
containerEl?.appendChild(clonedMenuEl!);
}}
{...}
/>
);
Don't forget to attach the closing animation to the right css class. In this case menu--close
@keyframes fadeOut {
0% {
opacity: 1;
transform: translateY(0);
}
100% {
opacity: 0;
transform: translateY(2rem);
}
}
.menu--close {
animation: fadeOut 0.2s ease-in-out;
}
In the end, you will have something like this
Another way is to use menuIsOpen props instead of cloning.
const [isMenuOpen, setIsMenuOpen] = useState(false)
const openMenuHandler = async () => {
await setIsMenuOpen(true)
const menu = document.querySelector(`#select .menu`)
menu.style.opacity = '1'
}
const closeMenuHandler = () => {
const menu = document.querySelector(`#select .menu`)
menu.style.opacity = '0'
setTimeout(() => {
setIsMenuOpen(false)
}, 400)
}
<Select
menuIsOpen={isMenuOpen}
onMenuOpen={() => {openMenuHandler()}}
onMenuClose={() => {closeMenuHandler()}}
/>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With