33 */
44import { useSelect } from '@wordpress/data' ;
55import { Button } from '@wordpress/components' ;
6+ import { useRef , useEffect , useState } from '@wordpress/element' ;
7+ import { ESCAPE , DOWN } from '@wordpress/keycodes' ;
68import { __ } from '@wordpress/i18n' ;
79import { wordpress } from '@wordpress/icons' ;
810
911function 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+
38136export default FullscreenModeClose ;
0 commit comments