Skip to content

Commit 9a9f9ef

Browse files
author
Jon Q
committed
Improve interaction handling for Navigation menu logo
Create custom React hook to encapsulate state, click, focus, and keyboard logic.
1 parent 9fcbae1 commit 9a9f9ef

File tree

1 file changed

+101
-3
lines changed
  • packages/edit-post/src/components/header/fullscreen-mode-close

1 file changed

+101
-3
lines changed

packages/edit-post/src/components/header/fullscreen-mode-close/index.js

Lines changed: 101 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,15 @@
33
*/
44
import { useSelect } from '@wordpress/data';
55
import { Button } from '@wordpress/components';
6+
import { useRef, useEffect, useState } from '@wordpress/element';
7+
import { ESCAPE, DOWN } from '@wordpress/keycodes';
68
import { __ } from '@wordpress/i18n';
79
import { wordpress } from '@wordpress/icons';
810

911
function FullscreenModeClose() {
12+
const buttonRef = useRef();
13+
const toggleAdminMenu = useToggleAdminMenu( { ref: buttonRef } );
14+
1015
const { isActive, postType } = useSelect( ( select ) => {
1116
const { getCurrentPostType } = select( 'core/editor' );
1217
const { isFeatureActive } = select( 'core/edit-post' );
@@ -27,12 +32,105 @@ function FullscreenModeClose() {
2732
className="edit-post-fullscreen-mode-close"
2833
icon={ wordpress }
2934
iconSize={ 36 }
30-
onClick={ function() {
31-
document.body.classList.toggle( 'is-showing-admin-menu' );
32-
} }
35+
onClick={ toggleAdminMenu }
3336
label={ __( 'Show sidebar menu' ) }
37+
ref={ buttonRef }
3438
/>
3539
);
3640
}
3741

42+
function useToggleAdminMenu( { ref } ) {
43+
const [ isActive, setIsActive ] = useState( false );
44+
45+
const navigationHeaderNode = document.querySelector( '.edit-post-header' );
46+
const adminMenuNode = document.querySelector( '#adminmenumain' );
47+
48+
const toggleClassName = 'is-showing-admin-menu';
49+
50+
const toggleAdminMenu = () => setIsActive( ! isActive );
51+
const closeAdminMenu = () => setIsActive( false );
52+
53+
const focusFirstAdminMenuItem = () => {
54+
const buttonNode = ref.current;
55+
if ( ! buttonNode ) return;
56+
57+
const isButtonFocused = buttonNode.matches( ':focus' );
58+
const item = adminMenuNode.querySelector( '#adminmenu > li > a' );
59+
60+
if ( isButtonFocused && item ) {
61+
item.focus();
62+
}
63+
};
64+
65+
// Renders the open/closed UI for the admin menu
66+
useEffect( () => {
67+
if ( isActive ) {
68+
document.body.classList.add( toggleClassName );
69+
} else {
70+
document.body.classList.remove( toggleClassName );
71+
}
72+
}, [ isActive ] );
73+
74+
// Handles closing the admin menu when clicking outside
75+
useEffect( () => {
76+
const handleOnClickOutside = ( event ) => {
77+
const { target } = event;
78+
79+
const didClickOutsideNavigationHeader =
80+
! navigationHeaderNode.contains( target ) &&
81+
target !== navigationHeaderNode;
82+
83+
const didClickOutsideAdminMenu =
84+
! adminMenuNode.contains( target ) && target !== adminMenuNode;
85+
86+
const didClickOutside =
87+
didClickOutsideNavigationHeader && didClickOutsideAdminMenu;
88+
89+
if ( didClickOutside ) {
90+
closeAdminMenu();
91+
}
92+
};
93+
94+
if ( isActive ) {
95+
document.body.addEventListener( 'click', handleOnClickOutside );
96+
}
97+
98+
return () => {
99+
if ( isActive ) {
100+
document.body.removeEventListener(
101+
'click',
102+
handleOnClickOutside
103+
);
104+
}
105+
};
106+
}, [ isActive ] );
107+
108+
// Handles closing the admin menu when pressing ESCAPE or DOWN
109+
useEffect( () => {
110+
const handleOnKeyDown = ( event ) => {
111+
const { keyCode } = event;
112+
113+
if ( keyCode === ESCAPE ) {
114+
closeAdminMenu();
115+
}
116+
117+
if ( keyCode === DOWN ) {
118+
focusFirstAdminMenuItem();
119+
}
120+
};
121+
122+
if ( isActive ) {
123+
document.body.addEventListener( 'keydown', handleOnKeyDown );
124+
}
125+
126+
return () => {
127+
if ( isActive ) {
128+
document.body.removeEventListener( 'keydown', handleOnKeyDown );
129+
}
130+
};
131+
}, [ isActive ] );
132+
133+
return toggleAdminMenu;
134+
}
135+
38136
export default FullscreenModeClose;

0 commit comments

Comments
 (0)