Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Keep the focus in the block navigation when user's last interaction was with the block navigation #22524

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,18 @@ _Returns_

- `boolean`: Whether an ancestor of the block is in multi-selection set.

<a name="isAutoFocusEnabled" href="#isAutoFocusEnabled">#</a> **isAutoFocusEnabled**

Returns whether the automatic focus is enabled.

_Parameters_

- _state_ `Object`: Editor state.

_Returns_

- `boolean`: Is automatic focus enabled.

<a name="isBlockHighlighted" href="#isBlockHighlighted">#</a> **isBlockHighlighted**

Returns true if the current highlighted block matches the block clientId.
Expand Down Expand Up @@ -1255,6 +1267,14 @@ _Parameters_

- _clientId_ `string`: Block client ID.

<a name="setAutoFocusEnabled" href="#setAutoFocusEnabled">#</a> **setAutoFocusEnabled**

Generators that triggers an action used to enable or disable the automatic focus.

_Parameters_

- _isAutoFocusEnabled_ `boolean`: Enable/Disable navigation mode.

<a name="setHasControlledInnerBlocks" href="#setHasControlledInnerBlocks">#</a> **setHasControlledInnerBlocks**

Returns an action object that sets whether the block has controlled innerblocks.
Expand Down
15 changes: 13 additions & 2 deletions packages/block-editor/src/components/block-list/block-wrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const BlockComponent = forwardRef(
const {
clientId,
rootClientId,
isAutoFocusEnabled,
isSelected,
isFirstMultiSelected,
isLastMultiSelected,
Expand Down Expand Up @@ -124,10 +125,20 @@ const BlockComponent = forwardRef(
};

useEffect( () => {
if ( ! isMultiSelecting && ! isNavigationMode && isSelected ) {
if (
! isMultiSelecting &&
! isNavigationMode &&
isSelected &&
isAutoFocusEnabled
) {
focusTabbable();
}
}, [ isSelected, isMultiSelecting, isNavigationMode ] );
}, [
isSelected,
isMultiSelecting,
isNavigationMode,
isAutoFocusEnabled,
] );

// Block Reordering animation
const animationStyle = useMovingAnimation(
Expand Down
4 changes: 4 additions & 0 deletions packages/block-editor/src/components/block-list/block.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ function BlockListBlock( {
toggleSelection,
index,
enableAnimation,
isAutoFocusEnabled,
isNavigationMode,
isMultiSelecting,
} ) {
Expand Down Expand Up @@ -159,6 +160,7 @@ function BlockListBlock( {
const value = {
clientId,
rootClientId,
isAutoFocusEnabled,
isSelected,
isFirstMultiSelected,
isLastMultiSelected,
Expand Down Expand Up @@ -218,6 +220,7 @@ const applyWithSelect = withSelect(
const {
isBlockSelected,
isAncestorMultiSelected,
isAutoFocusEnabled,
isBlockMultiSelected,
isFirstMultiSelectedBlock,
getLastMultiSelectedBlockClientId,
Expand Down Expand Up @@ -251,6 +254,7 @@ const applyWithSelect = withSelect(
// Do not add new properties here, use `useSelect` instead to avoid
// leaking new props to the public API (editor.BlockListBlock filter).
return {
isAutoFocusEnabled: isAutoFocusEnabled(),
isMultiSelected: isBlockMultiSelected( clientId ),
isPartOfMultiSelection:
isBlockMultiSelected( clientId ) ||
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ const BlockNavigationBlockContents = forwardRef(

return withBlockNavigationSlots ? (
<BlockNavigationBlockSlot
ref={ ref }
className="block-editor-block-navigation-block-contents"
block={ block }
onClick={ onClick }
Expand All @@ -30,10 +29,10 @@ const BlockNavigationBlockContents = forwardRef(
siblingCount={ siblingCount }
level={ level }
{ ...props }
ref={ ref }
/>
) : (
<BlockNavigationBlockSelectButton
ref={ ref }
className="block-editor-block-navigation-block-contents"
block={ block }
onClick={ onClick }
Expand All @@ -42,6 +41,7 @@ const BlockNavigationBlockContents = forwardRef(
siblingCount={ siblingCount }
level={ level }
{ ...props }
ref={ ref }
/>
);
}
Expand Down
28 changes: 27 additions & 1 deletion packages/block-editor/src/components/block-navigation/block.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import classnames from 'classnames';
*/
import { __experimentalTreeGridCell as TreeGridCell } from '@wordpress/components';
import { moreVertical } from '@wordpress/icons';
import { useState } from '@wordpress/element';
import { useState, useEffect, useRef } from '@wordpress/element';
import { useSelect } from '@wordpress/data';

/**
* Internal dependencies
Expand All @@ -34,6 +35,7 @@ export default function BlockNavigationBlock( {
terminatedLevels,
path,
} ) {
const ref = useRef();
const [ isHovered, setIsHovered ] = useState( false );
const [ isFocused, setIsFocused ] = useState( false );
const { clientId } = block;
Expand All @@ -51,11 +53,34 @@ export default function BlockNavigationBlock( {
__experimentalWithEllipsisMenu: withEllipsisMenu,
__experimentalWithEllipsisMenuMinLevel: ellipsisMenuMinLevel,
} = useBlockNavigationContext();

const ellipsisMenuClassName = classnames(
'block-editor-block-navigation-block__menu-cell',
{ 'is-visible': hasVisibleMovers }
);

const { isEditorAutoFocusEnabled } = useSelect( ( select ) => ( {
isEditorAutoFocusEnabled: select(
'core/block-editor'
).isAutoFocusEnabled(),
} ) );
useEffect( () => {
let timeout;
if ( ! isEditorAutoFocusEnabled && isSelected ) {
// Give slots time to settle down
timeout = setTimeout( function() {
// Select the new block
onClick();

// Move focus to the new block
if ( ref.current ) {
ref.current.focus();
}
} );
}
return () => clearTimeout( timeout );
}, [ isEditorAutoFocusEnabled, isSelected ] );

return (
<BlockNavigationLeaf
className={ classnames( {
Expand Down Expand Up @@ -89,6 +114,7 @@ export default function BlockNavigationBlock( {
siblingCount={ siblingCount }
level={ level }
{ ...props }
ref={ ref }
/>
</div>
) }
Expand Down
12 changes: 12 additions & 0 deletions packages/block-editor/src/store/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -883,6 +883,18 @@ export function* setNavigationMode( isNavigationMode = true ) {
}
}

/**
* Generators that triggers an action used to enable or disable the automatic focus.
*
* @param {boolean} isAutoFocusEnabled Enable/Disable navigation mode.
*/
export function* setAutoFocusEnabled( isAutoFocusEnabled = true ) {
yield {
type: 'SET_AUTO_FOCUS_ENABLED',
isAutoFocusEnabled,
};
}

/**
* Generator that triggers an action used to duplicate a list of blocks.
*
Expand Down
17 changes: 17 additions & 0 deletions packages/block-editor/src/store/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -1519,6 +1519,22 @@ export function isNavigationMode( state = false, action ) {
return state;
}

/**
* Reducer returning whether the automatic focus should be enabled or not.
*
* @param {boolean} state Current state.
* @param {Object} action Dispatched action.
*
* @return {string} Updated state.
*/
export function isAutoFocusEnabled( state = true, action ) {
if ( action.type === 'SET_AUTO_FOCUS_ENABLED' ) {
return action.isAutoFocusEnabled;
}

return state;
}

/**
* Reducer return an updated state representing the most recent block attribute
* update. The state is structured as an object where the keys represent the
Expand Down Expand Up @@ -1621,6 +1637,7 @@ export default combineReducers( {
preferences,
lastBlockAttributesChange,
isNavigationMode,
isAutoFocusEnabled,
automaticChangeStatus,
highlightedBlock,
} );
11 changes: 11 additions & 0 deletions packages/block-editor/src/store/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -1627,6 +1627,17 @@ export function isNavigationMode( state ) {
return state.isNavigationMode;
}

/**
* Returns whether the automatic focus is enabled.
*
* @param {Object} state Editor state.
*
* @return {boolean} Is automatic focus enabled.
*/
export function isAutoFocusEnabled( state ) {
return state.isAutoFocusEnabled;
}

/**
* Returns true if the last change was an automatic change, false otherwise.
*
Expand Down
4 changes: 2 additions & 2 deletions packages/components/src/panel/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ import classnames from 'classnames';
*/
import PanelHeader from './header';

function Panel( { header, className, children } ) {
function Panel( { header, className, children, ...props } ) {
const classNames = classnames( className, 'components-panel' );
return (
<div className={ classNames }>
<div className={ classNames } { ...props }>
{ header && <PanelHeader label={ header } /> }
{ children }
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export default function BlockEditorPanel( {
onDeleteMenu,
menuId,
saveBlocks,
...props
} ) {
const { isNavigationModeActive, hasSelectedBlock } = useSelect(
( select ) => {
Expand All @@ -48,7 +49,10 @@ export default function BlockEditorPanel( {
);

return (
<Panel className="edit-navigation-menu-editor__block-editor-panel">
<Panel
className="edit-navigation-menu-editor__block-editor-panel"
{ ...props }
>
<PanelBody title={ __( 'Navigation menu' ) }>
<div className="components-panel__header-actions">
<Button isPrimary onClick={ saveBlocks }>
Expand Down
56 changes: 47 additions & 9 deletions packages/edit-navigation/src/components/menu-editor/index.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
/**
* WordPress dependencies
*/
import { useDispatch } from '@wordpress/data';
import {
BlockEditorKeyboardShortcuts,
BlockEditorProvider,
} from '@wordpress/block-editor';
import { useViewportMatch } from '@wordpress/compose';
import { useState, useEffect } from '@wordpress/element';

/**
* Internal dependencies
Expand All @@ -21,7 +23,6 @@ export default function MenuEditor( {
onDeleteMenu,
} ) {
const [ blocks, setBlocks, saveBlocks ] = useNavigationBlocks( menuId );
const isLargeViewport = useViewportMatch( 'medium' );

return (
<div className="edit-navigation-menu-editor">
Expand All @@ -38,18 +39,55 @@ export default function MenuEditor( {
hasFixedToolbar: true,
} }
>
<BlockEditorKeyboardShortcuts />
<MenuEditorShortcuts saveBlocks={ saveBlocks } />
<NavigationStructurePanel
blocks={ blocks }
initialOpen={ isLargeViewport }
/>
<BlockEditorPanel
saveBlocks={ saveBlocks }
<MenuEditorBody
menuId={ menuId }
onDeleteMenu={ onDeleteMenu }
blocks={ blocks }
saveBlocks={ saveBlocks }
/>
</BlockEditorProvider>
</div>
);
}

/*
* This has to be a separate component because core/block-editor data is only available for descendants
* of BlockEditorProvider
*/
const MenuEditorBody = ( { menuId, onDeleteMenu, blocks, saveBlocks } ) => {
const { setAutoFocusEnabled } = useDispatch( 'core/block-editor' );
const isLargeViewport = useViewportMatch( 'medium' );
const [ lastInteractedSection, setLastInteractedSection ] = useState(
null
);

useEffect( () => {
if ( lastInteractedSection === 'navigation' ) {
setAutoFocusEnabled( false );
} else if ( lastInteractedSection === 'editor' ) {
setAutoFocusEnabled( true );
}
}, [ lastInteractedSection ] );

return (
<>
<BlockEditorKeyboardShortcuts />
<MenuEditorShortcuts saveBlocks={ saveBlocks } />
<NavigationStructurePanel
blocks={ blocks }
initialOpen={ isLargeViewport }
onMouseDown={ () => {
setLastInteractedSection( 'navigation' );
} }
/>
<BlockEditorPanel
saveBlocks={ saveBlocks }
menuId={ menuId }
onDeleteMenu={ onDeleteMenu }
onMouseDown={ () => {
setLastInteractedSection( 'editor' );
} }
/>
</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ import { Panel, PanelBody } from '@wordpress/components';
import { useDispatch, useSelect } from '@wordpress/data';
import { __ } from '@wordpress/i18n';

export default function NavigationStructurePanel( { blocks, initialOpen } ) {
export default function NavigationStructurePanel( {
blocks,
initialOpen,
...props
} ) {
const selectedBlockClientIds = useSelect(
( select ) => select( 'core/block-editor' ).getSelectedBlockClientIds(),
[]
Expand All @@ -15,7 +19,10 @@ export default function NavigationStructurePanel( { blocks, initialOpen } ) {
const showNavigationStructure = !! blocks.length;

return (
<Panel className="edit-navigation-menu-editor__navigation-structure-panel">
<Panel
className="edit-navigation-menu-editor__navigation-structure-panel"
{ ...props }
>
<PanelBody
title={ __( 'Navigation structure' ) }
initialOpen={ initialOpen }
Expand Down