@@ -6,6 +6,7 @@ import { Button } from '../Button';
66import { DropdownTriggerRenderFunction } from './DropdownTrigger' ;
77import { shortcuts } from '../HotKeyWrapper' ;
88import { Shortcut } from '../Shortcut' ;
9+ import { transition } from '../../helpers/transition' ;
910
1011export const DIVIDER = 'divider' as const ;
1112
@@ -96,13 +97,17 @@ export function DropdownMenu({
9697 const dropdownRef = useRef < HTMLDivElement > ( null ) ;
9798 const triggerRef = useRef < HTMLButtonElement > ( null ) ;
9899 const [ isActive , setIsActive ] = useState ( false ) ;
100+ const [ visible , setVisible ] = useState ( false ) ;
99101
100102 const handleClose = useCallback ( ( ) => {
101- setIsActive ( false ) ;
103+ setVisible ( false ) ;
102104 // Whenever the menu closes, assume that the next one will be opened with mouse
103105 setUseKeys ( false ) ;
104106 // Always reset to the top item on close
105- setSelectedIndex ( 0 ) ;
107+ setTimeout ( ( ) => {
108+ setIsActive ( false ) ;
109+ setSelectedIndex ( 0 ) ;
110+ } , 100 ) ;
106111 } , [ ] ) ;
107112
108113 useClickAwayListener ( [ triggerRef , dropdownRef ] , handleClose , isActive , [
@@ -126,27 +131,31 @@ export function DropdownMenu({
126131 return ;
127132 }
128133
129- const triggerRect = triggerRef . current ! . getBoundingClientRect ( ) ;
130- const menuRect = dropdownRef . current ! . getBoundingClientRect ( ) ;
131- const topPos = triggerRect . y - menuRect . height ;
134+ setIsActive ( true ) ;
132135
133- // If the top is outside of the screen, render it below
134- if ( topPos < 0 ) {
135- setY ( triggerRect . y + triggerRect . height / 2 ) ;
136- } else {
137- setY ( topPos + triggerRect . height / 2 ) ;
138- }
136+ requestAnimationFrame ( ( ) => {
137+ const triggerRect = triggerRef . current ! . getBoundingClientRect ( ) ;
138+ const menuRect = dropdownRef . current ! . getBoundingClientRect ( ) ;
139+ const topPos = triggerRect . y - menuRect . height ;
139140
140- const leftPos = triggerRect . x - menuRect . width ;
141+ // If the top is outside of the screen, render it below
142+ if ( topPos < 0 ) {
143+ setY ( triggerRect . y + triggerRect . height / 2 ) ;
144+ } else {
145+ setY ( topPos + triggerRect . height / 2 ) ;
146+ }
141147
142- // If the left is outside of the screen, render it to the right
143- if ( leftPos < 0 ) {
144- setX ( triggerRect . x ) ;
145- } else {
146- setX ( triggerRect . x - menuRect . width + triggerRect . width ) ;
147- }
148+ const leftPos = triggerRect . x - menuRect . width ;
148149
149- setIsActive ( true ) ;
150+ // If the left is outside of the screen, render it to the right
151+ if ( leftPos < 0 ) {
152+ setX ( triggerRect . x ) ;
153+ } else {
154+ setX ( triggerRect . x - menuRect . width + triggerRect . width ) ;
155+ }
156+
157+ setVisible ( true ) ;
158+ } ) ;
150159 } , [ isActive ] ) ;
151160
152161 const handleTriggerClick = useCallback ( ( ) => {
@@ -219,7 +228,7 @@ export function DropdownMenu({
219228 isActive = { isActive }
220229 menuId = { menuId }
221230 />
222- < Menu ref = { dropdownRef } isActive = { isActive } x = { x } y = { y } id = { menuId } >
231+ < Menu ref = { dropdownRef } visible = { visible } x = { x } y = { y } id = { menuId } >
223232 { isActive &&
224233 normalizedItems . map ( ( props , i ) => {
225234 if ( ! isItem ( props ) ) {
@@ -252,12 +261,6 @@ export function DropdownMenu({
252261 ) ;
253262}
254263
255- interface MenuProps {
256- isActive : boolean ;
257- x : number ;
258- y : number ;
259- }
260-
261264export interface MenuItemSidebarProps extends MenuItemMinimial {
262265 handleClickItem ?: ( ) => unknown ;
263266}
@@ -343,6 +346,12 @@ const ItemDivider = styled.div`
343346 border-bottom: 1px solid ${ p => p . theme . colors . bg2 } ;
344347` ;
345348
349+ interface MenuProps {
350+ visible : boolean ;
351+ x : number ;
352+ y : number ;
353+ }
354+
346355const Menu = styled . div < MenuProps > `
347356 font-size: ${ p => p . theme . fontSizeBody } rem;
348357 overflow: hidden;
@@ -358,6 +367,7 @@ const Menu = styled.div<MenuProps>`
358367 left: ${ p => p . x } px;
359368 width: auto;
360369 box-shadow: ${ p => p . theme . boxShadowSoft } ;
361- opacity: ${ p => ( p . isActive ? 1 : 0 ) } ;
362- visibility: ${ p => ( p . isActive ? 'visible' : 'hidden' ) } ;
370+ opacity: ${ p => ( p . visible ? 1 : 0 ) } ;
371+
372+ transition: ${ ( ) => transition ( 'opacity' ) } ;
363373` ;
0 commit comments