diff --git a/packages/edit-widgets/src/components/header/document-tools/index.js b/packages/edit-widgets/src/components/header/document-tools/index.js index bd1b8fc7adae7..a0d69cde376cf 100644 --- a/packages/edit-widgets/src/components/header/document-tools/index.js +++ b/packages/edit-widgets/src/components/header/document-tools/index.js @@ -4,12 +4,9 @@ import { useSelect, useDispatch } from '@wordpress/data'; import { __, _x } from '@wordpress/i18n'; import { Button, ToolbarItem } from '@wordpress/components'; -import { - NavigableToolbar, - store as blockEditorStore, -} from '@wordpress/block-editor'; +import { NavigableToolbar } from '@wordpress/block-editor'; import { listView, plus } from '@wordpress/icons'; -import { useCallback, useRef } from '@wordpress/element'; +import { useCallback } from '@wordpress/element'; import { useViewportMatch } from '@wordpress/compose'; /** @@ -17,61 +14,44 @@ import { useViewportMatch } from '@wordpress/compose'; */ import UndoButton from '../undo-redo/undo'; import RedoButton from '../undo-redo/redo'; -import useLastSelectedWidgetArea from '../../../hooks/use-last-selected-widget-area'; import { store as editWidgetsStore } from '../../../store'; import { unlock } from '../../../lock-unlock'; function DocumentTools() { const isMediumViewport = useViewportMatch( 'medium' ); - const inserterButton = useRef(); - const widgetAreaClientId = useLastSelectedWidgetArea(); - const isLastSelectedWidgetAreaOpen = useSelect( - ( select ) => - select( editWidgetsStore ).getIsWidgetAreaOpen( - widgetAreaClientId - ), - [ widgetAreaClientId ] - ); - const { isInserterOpen, isListViewOpen, listViewToggleRef } = useSelect( - ( select ) => { - const { isInserterOpened, isListViewOpened, getListViewToggleRef } = - unlock( select( editWidgetsStore ) ); - return { - isInserterOpen: isInserterOpened(), - isListViewOpen: isListViewOpened(), - listViewToggleRef: getListViewToggleRef(), - }; - }, - [] - ); - const { setIsWidgetAreaOpen, setIsInserterOpened, setIsListViewOpened } = + + const { + isInserterOpen, + isListViewOpen, + inserterSidebarToggleRef, + listViewToggleRef, + } = useSelect( ( select ) => { + const { + isInserterOpened, + getInserterSidebarToggleRef, + isListViewOpened, + getListViewToggleRef, + } = unlock( select( editWidgetsStore ) ); + return { + isInserterOpen: isInserterOpened(), + isListViewOpen: isListViewOpened(), + inserterSidebarToggleRef: getInserterSidebarToggleRef(), + listViewToggleRef: getListViewToggleRef(), + }; + }, [] ); + const { setIsInserterOpened, setIsListViewOpened } = useDispatch( editWidgetsStore ); - const { selectBlock } = useDispatch( blockEditorStore ); - const handleClick = () => { - if ( isInserterOpen ) { - // Focusing the inserter button closes the inserter popover. - setIsInserterOpened( false ); - } else { - if ( ! isLastSelectedWidgetAreaOpen ) { - // Select the last selected block if hasn't already. - selectBlock( widgetAreaClientId ); - // Open the last selected widget area when opening the inserter. - setIsWidgetAreaOpen( widgetAreaClientId, true ); - } - // The DOM updates resulting from selectBlock() and setIsInserterOpened() calls are applied the - // same tick and pretty much in a random order. The inserter is closed if any other part of the - // app receives focus. If selectBlock() happens to take effect after setIsInserterOpened() then - // the inserter is visible for a brief moment and then gets auto-closed due to focus moving to - // the selected block. - window.requestAnimationFrame( () => setIsInserterOpened( true ) ); - } - }; const toggleListView = useCallback( () => setIsListViewOpened( ! isListViewOpen ), [ setIsListViewOpened, isListViewOpen ] ); + const toggleInserterSidebar = useCallback( + () => setIsInserterOpened( ! isInserterOpen ), + [ setIsInserterOpened, isInserterOpen ] + ); + return ( { event.preventDefault(); } } - onClick={ handleClick } + onClick={ toggleInserterSidebar } icon={ plus } /* translators: button label text should, if possible, be under 16 characters. */ diff --git a/packages/edit-widgets/src/store/private-selectors.js b/packages/edit-widgets/src/store/private-selectors.js index fca6aa5ddb759..ee2000ac12844 100644 --- a/packages/edit-widgets/src/store/private-selectors.js +++ b/packages/edit-widgets/src/store/private-selectors.js @@ -1,3 +1,7 @@ export function getListViewToggleRef( state ) { return state.listViewToggleRef; } + +export function getInserterSidebarToggleRef( state ) { + return state.inserterSidebarToggleRef; +} diff --git a/packages/edit-widgets/src/store/reducer.js b/packages/edit-widgets/src/store/reducer.js index 64bd6b4e0400e..3dbc95123a382 100644 --- a/packages/edit-widgets/src/store/reducer.js +++ b/packages/edit-widgets/src/store/reducer.js @@ -79,8 +79,21 @@ export function listViewToggleRef( state = { current: null } ) { return state; } +/** + * This reducer does nothing aside initializing a ref to the inserter sidebar toggle. + * We will have a unique ref per "editor" instance. + * + * @param {Object} state + * @return {Object} Reference to the inserter sidebar toggle button. + */ +export function inserterSidebarToggleRef( state = { current: null } ) { + return state; +} + export default combineReducers( { blockInserterPanel, + inserterSidebarToggleRef, listViewPanel, + listViewToggleRef, widgetAreasOpenState, } ); diff --git a/packages/editor/src/components/document-tools/index.js b/packages/editor/src/components/document-tools/index.js index 87202015136a2..6952fa34e31ae 100644 --- a/packages/editor/src/components/document-tools/index.js +++ b/packages/editor/src/components/document-tools/index.js @@ -16,7 +16,7 @@ import { } from '@wordpress/block-editor'; import { Button, ToolbarItem } from '@wordpress/components'; import { listView, plus } from '@wordpress/icons'; -import { useRef, useCallback } from '@wordpress/element'; +import { useCallback } from '@wordpress/element'; import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts'; import { store as preferencesStore } from '@wordpress/preferences'; @@ -38,7 +38,6 @@ function DocumentTools( { // This is a temporary prop until the list view is fully unified between post and site editors. listViewLabel = __( 'Document Overview' ), } ) { - const inserterButton = useRef(); const { setIsInserterOpened, setIsListViewOpened } = useDispatch( editorStore ); const { @@ -46,14 +45,19 @@ function DocumentTools( { isInserterOpened, isListViewOpen, listViewShortcut, + inserterSidebarToggleRef, listViewToggleRef, hasFixedToolbar, showIconLabels, } = useSelect( ( select ) => { const { getSettings } = select( blockEditorStore ); const { get } = select( preferencesStore ); - const { isListViewOpened, getListViewToggleRef, getEditorMode } = - unlock( select( editorStore ) ); + const { + isListViewOpened, + getEditorMode, + getInserterSidebarToggleRef, + getListViewToggleRef, + } = unlock( select( editorStore ) ); const { getShortcutRepresentation } = select( keyboardShortcutsStore ); const { __unstableGetEditorMode } = select( blockEditorStore ); @@ -63,6 +67,7 @@ function DocumentTools( { listViewShortcut: getShortcutRepresentation( 'core/editor/toggle-list-view' ), + inserterSidebarToggleRef: getInserterSidebarToggleRef(), listViewToggleRef: getListViewToggleRef(), hasFixedToolbar: getSettings().hasFixedToolbar, showIconLabels: get( 'core', 'showIconLabels' ), @@ -83,17 +88,10 @@ function DocumentTools( { [ setIsListViewOpened, isListViewOpen ] ); - const toggleInserter = useCallback( () => { - if ( isInserterOpened ) { - // Focusing the inserter button should close the inserter popover. - // However, there are some cases it won't close when the focus is lost. - // See https://github.com/WordPress/gutenberg/issues/43090 for more details. - inserterButton.current.focus(); - setIsInserterOpened( false ); - } else { - setIsInserterOpened( true ); - } - }, [ isInserterOpened, setIsInserterOpened ] ); + const toggleInserter = useCallback( + () => setIsInserterOpened( ! isInserterOpened ), + [ isInserterOpened, setIsInserterOpened ] + ); /* translators: button label text should, if possible, be under 16 characters. */ const longLabel = _x( @@ -119,7 +117,7 @@ function DocumentTools( {
{ ! isDistractionFree && ( { - const { getInsertionPoint } = unlock( select( editorStore ) ); - const { - getBlockRootClientId, - __unstableGetEditorMode, - getSettings, - } = select( blockEditorStore ); - const { get } = select( preferencesStore ); - const getBlockSectionRootClientId = () => { - if ( __unstableGetEditorMode() === 'zoom-out' ) { - const { sectionRootClientId } = unlock( getSettings() ); - if ( sectionRootClientId ) { - return sectionRootClientId; - } + const { + blockSectionRootClientId, + inserterSidebarToggleRef, + insertionPoint, + showMostUsedBlocks, + } = useSelect( ( select ) => { + const { getInserterSidebarToggleRef, getInsertionPoint } = unlock( + select( editorStore ) + ); + const { getBlockRootClientId, __unstableGetEditorMode, getSettings } = + select( blockEditorStore ); + const { get } = select( preferencesStore ); + const getBlockSectionRootClientId = () => { + if ( __unstableGetEditorMode() === 'zoom-out' ) { + const { sectionRootClientId } = unlock( getSettings() ); + if ( sectionRootClientId ) { + return sectionRootClientId; } - return getBlockRootClientId(); - }; - return { - insertionPoint: getInsertionPoint(), - showMostUsedBlocks: get( 'core', 'mostUsedBlocks' ), - blockSectionRootClientId: getBlockSectionRootClientId(), - }; - }, [] ); + } + return getBlockRootClientId(); + }; + return { + inserterSidebarToggleRef: getInserterSidebarToggleRef(), + insertionPoint: getInsertionPoint(), + showMostUsedBlocks: get( 'core', 'mostUsedBlocks' ), + blockSectionRootClientId: getBlockSectionRootClientId(), + }; + }, [] ); const { setIsInserterOpened } = useDispatch( editorStore ); const isMobileViewport = useViewportMatch( 'medium', '<' ); const libraryRef = useRef(); + // When closing the inserter, focus should return to the toggle button. + const closeInserterSidebar = useCallback( () => { + setIsInserterOpened( false ); + inserterSidebarToggleRef.current?.focus(); + }, [ inserterSidebarToggleRef, setIsInserterOpened ] ); + + const closeOnEscape = useCallback( + ( event ) => { + if ( event.keyCode === ESCAPE && ! event.defaultPrevented ) { + event.preventDefault(); + closeInserterSidebar(); + } + }, + [ closeInserterSidebar ] + ); + return ( -
+ // eslint-disable-next-line jsx-a11y/no-static-element-interactions +
setIsInserterOpened( false ) } + onClose={ closeInserterSidebar } />
diff --git a/packages/editor/src/store/private-selectors.js b/packages/editor/src/store/private-selectors.js index 5abd72f13713b..aa2af9172ff18 100644 --- a/packages/editor/src/store/private-selectors.js +++ b/packages/editor/src/store/private-selectors.js @@ -75,6 +75,9 @@ export const getInsertionPoint = createRegistrySelector( ( select ) => export function getListViewToggleRef( state ) { return state.listViewToggleRef; } +export function getInserterSidebarToggleRef( state ) { + return state.inserterSidebarToggleRef; +} const CARD_ICONS = { wp_block: symbol, wp_navigation: navigation, diff --git a/packages/editor/src/store/reducer.js b/packages/editor/src/store/reducer.js index 202baa1e7e5cb..f9b4e05ffa8e5 100644 --- a/packages/editor/src/store/reducer.js +++ b/packages/editor/src/store/reducer.js @@ -360,6 +360,17 @@ export function listViewToggleRef( state = { current: null } ) { return state; } +/** + * This reducer does nothing aside initializing a ref to the inserter sidebar toggle. + * We will have a unique ref per "editor" instance. + * + * @param {Object} state + * @return {Object} Reference to the inserter sidebar toggle button. + */ +export function inserterSidebarToggleRef( state = { current: null } ) { + return state; +} + export function publishSidebarActive( state = false, action ) { switch ( action.type ) { case 'OPEN_PUBLISH_SIDEBAR': @@ -387,6 +398,7 @@ export default combineReducers( { deviceType, removedPanels, blockInserterPanel, + inserterSidebarToggleRef, listViewPanel, listViewToggleRef, publishSidebarActive,