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

Better regions and shortcut #45157

Open
wants to merge 2 commits into
base: trunk
Choose a base branch
from
Open
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 @@ -29,20 +29,13 @@ import { store as blockEditorStore } from '../../../store';
function useInitialPosition( clientId ) {
return useSelect(
( select ) => {
const {
getSelectedBlocksInitialCaretPosition,
__unstableGetEditorMode,
isBlockSelected,
} = select( blockEditorStore );
const { getSelectedBlocksInitialCaretPosition, isBlockSelected } =
select( blockEditorStore );

if ( ! isBlockSelected( clientId ) ) {
return;
}

if ( __unstableGetEditorMode() !== 'edit' ) {
return;
}

// If there's no initial position, return 0 to focus the start.
return getSelectedBlocksInitialCaretPosition();
},
Expand All @@ -61,14 +54,19 @@ function useInitialPosition( clientId ) {
export function useFocusFirstElement( clientId ) {
const ref = useRef();
const initialPosition = useInitialPosition( clientId );
const { isBlockSelected, isMultiSelecting } = useSelect( blockEditorStore );
const { isBlockSelected, isMultiSelecting, __unstableGetEditorMode } =
useSelect( blockEditorStore );

useEffect( () => {
// Check if the block is still selected at the time this effect runs.
if ( ! isBlockSelected( clientId ) || isMultiSelecting() ) {
return;
}

if ( __unstableGetEditorMode() !== 'edit' ) {
return;
}

if ( initialPosition === undefined || initialPosition === null ) {
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,9 +169,12 @@ function BlockSelectionButton( { clientId, rootClientId } ) {
selectedBlockClientId;
}
const startingBlockClientId = hasBlockMovingClientId();
if ( isEscape && startingBlockClientId && ! event.defaultPrevented ) {
setBlockMovingClientId( null );
event.preventDefault();
if ( isEscape && ! event.defaultPrevented ) {
if ( startingBlockClientId && ! event.shiftKey ) {
setBlockMovingClientId( null );
event.preventDefault();
}
return;
}
if ( ( isEnter || isSpace ) && startingBlockClientId ) {
const sourceRoot = getBlockRootClientId( startingBlockClientId );
Expand Down Expand Up @@ -275,6 +278,9 @@ function BlockSelectionButton( { clientId, rootClientId } ) {
: undefined
}
onKeyDown={ onKeyDown }
onBlur={ () => {
setNavigationMode( false );
} }
label={ label }
showTooltip={ false }
className="block-selection-button_select-button"
Expand Down
4 changes: 4 additions & 0 deletions packages/block-editor/src/components/block-tools/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
default as InsertionPoint,
} from './insertion-point';
import SelectedBlockPopover from './selected-block-popover';
import NavBlockPopover from './nav-block-popover';
import { store as blockEditorStore } from '../../store';
import BlockContextualToolbar from './block-contextual-toolbar';
import usePopoverScroll from '../block-popover/use-popover-scroll';
Expand Down Expand Up @@ -146,6 +147,9 @@ export default function BlockTools( {
name="__unstable-block-tools-after"
ref={ blockToolbarAfterRef }
/>
<NavBlockPopover
__unstableContentRef={ __unstableContentRef }
/>
{ isZoomOutMode && (
<ZoomOutModeInserters
__unstableContentRef={ __unstableContentRef }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
/**
* External dependencies
*/
import { find } from 'lodash';
import classnames from 'classnames';

/**
* WordPress dependencies
*/
/**
* WordPress dependencies
*/
import { useRef, useEffect } from '@wordpress/element';
import { isUnmodifiedDefaultBlock } from '@wordpress/blocks';
import { useDispatch, useSelect } from '@wordpress/data';
import { useShortcut } from '@wordpress/keyboard-shortcuts';
import {
useMergeRefs,
useRefEffect,
useViewportMatch,
} from '@wordpress/compose';

/**
* Internal dependencies
*/
/**
* Internal dependencies
*/
import BlockSelectionButton from './block-selection-button';
import { store as blockEditorStore } from '../../store';
import BlockPopover from '../block-popover';
import useBlockToolbarPopoverProps from './use-block-toolbar-popover-props';

function selector( select ) {
const {
__unstableGetEditorMode,
isMultiSelecting,
hasMultiSelection,
isTyping,
getSettings,
getLastMultiSelectedBlockClientId,
} = select( blockEditorStore );
return {
editorMode: __unstableGetEditorMode(),
isMultiSelecting: isMultiSelecting(),
isTyping: isTyping(),
hasFixedToolbar: getSettings().hasFixedToolbar,
isDistractionFree: getSettings().isDistractionFree,
lastClientId: hasMultiSelection()
? getLastMultiSelectedBlockClientId()
: null,
};
}

function SelectedBlockPopover( {
clientId,
rootClientId,
isEmptyDefaultBlock,
capturingClientId,
__unstablePopoverSlot,
__unstableContentRef,
} ) {
const {
editorMode,
isMultiSelecting,
isTyping,
hasFixedToolbar,
isDistractionFree,
lastClientId,
} = useSelect( selector, [] );
const isInsertionPointVisible = useSelect(
( select ) => {
const {
isBlockInsertionPointVisible,
getBlockInsertionPoint,
getBlockOrder,
} = select( blockEditorStore );

if ( ! isBlockInsertionPointVisible() ) {
return false;
}

const insertionPoint = getBlockInsertionPoint();
const order = getBlockOrder( insertionPoint.rootClientId );
return order[ insertionPoint.index ] === clientId;
},
[ clientId ]
);
const isLargeViewport = useViewportMatch( 'medium' );
const isToolbarForced = useRef( false );
const { stopTyping, setNavigationMode } = useDispatch( blockEditorStore );

const showEmptyBlockSideInserter =
! isTyping && editorMode === 'edit' && isEmptyDefaultBlock;
const shouldShowBreadcrumb =
editorMode === 'navigation' || editorMode === 'zoom-out';
const shouldShowContextualToolbar =
editorMode === 'edit' &&
! hasFixedToolbar &&
isLargeViewport &&
! isMultiSelecting &&
! showEmptyBlockSideInserter &&
! isTyping;
const canFocusHiddenToolbar =
editorMode === 'edit' &&
! shouldShowContextualToolbar &&
! hasFixedToolbar &&
! isDistractionFree &&
! isEmptyDefaultBlock;

useShortcut(
'core/block-editor/focus-toolbar',
() => {
isToolbarForced.current = true;
stopTyping( true );
},
{
isDisabled: ! canFocusHiddenToolbar,
}
);

useEffect( () => {
isToolbarForced.current = false;
} );

const popoverProps = useBlockToolbarPopoverProps( {
contentElement: __unstableContentRef?.current,
clientId,
} );

// onFocus doesn't work on Popover. Should be fixed.
const ref = useMergeRefs( [
popoverProps.ref,
useRefEffect( ( node ) => {
function onFocus() {
setNavigationMode( true );
}
node.addEventListener( 'focus', onFocus );
return () => {
node.removeEventListener( 'focus', onFocus );
};
}, [] ),
] );

if ( ! shouldShowBreadcrumb && ! shouldShowContextualToolbar ) {
return null;
}

return (
<BlockPopover
clientId={ capturingClientId || clientId }
bottomClientId={ lastClientId }
className={ classnames( 'block-editor-block-list__block-popover', {
'is-insertion-point-visible': isInsertionPointVisible,
} ) }
__unstablePopoverSlot={ __unstablePopoverSlot }
__unstableContentRef={ __unstableContentRef }
resize={ false }
{ ...popoverProps }
ref={ ref }
role="region"
tabIndex="-1"
>
{ shouldShowBreadcrumb && (
<BlockSelectionButton
clientId={ clientId }
rootClientId={ rootClientId }
/>
) }
</BlockPopover>
);
}

function wrapperSelector( select ) {
const {
getSelectedBlockClientId,
getFirstMultiSelectedBlockClientId,
getBlockRootClientId,
getBlock,
getBlockParents,
getSettings,
isNavigationMode: _isNavigationMode,
__experimentalGetBlockListSettingsForBlocks,
} = select( blockEditorStore );

const clientId =
getSelectedBlockClientId() || getFirstMultiSelectedBlockClientId();

if ( ! clientId ) {
return;
}

const { name, attributes = {} } = getBlock( clientId ) || {};
const blockParentsClientIds = getBlockParents( clientId );

// Get Block List Settings for all ancestors of the current Block clientId.
const parentBlockListSettings = __experimentalGetBlockListSettingsForBlocks(
blockParentsClientIds
);

// Get the clientId of the topmost parent with the capture toolbars setting.
const capturingClientId = find(
blockParentsClientIds,
( parentClientId ) =>
parentBlockListSettings[ parentClientId ]
?.__experimentalCaptureToolbars
);

const settings = getSettings();

return {
clientId,
rootClientId: getBlockRootClientId( clientId ),
name,
isDistractionFree: settings.isDistractionFree,
isNavigationMode: _isNavigationMode(),
isEmptyDefaultBlock:
name && isUnmodifiedDefaultBlock( { name, attributes } ),
capturingClientId,
};
}

export default function WrappedBlockPopover( {
__unstablePopoverSlot,
__unstableContentRef,
} ) {
const selected = useSelect( wrapperSelector, [] );

if ( ! selected ) {
return null;
}

const {
clientId,
rootClientId,
name,
isEmptyDefaultBlock,
capturingClientId,
isDistractionFree,
isNavigationMode,
} = selected;

if ( ! name ) {
return null;
}

return (
<SelectedBlockPopover
clientId={ clientId }
rootClientId={ rootClientId }
isEmptyDefaultBlock={ isEmptyDefaultBlock }
showContents={ ! isDistractionFree || isNavigationMode }
capturingClientId={ capturingClientId }
__unstablePopoverSlot={ __unstablePopoverSlot }
__unstableContentRef={ __unstableContentRef }
/>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import { useViewportMatch } from '@wordpress/compose';
/**
* Internal dependencies
*/
import BlockSelectionButton from './block-selection-button';
import BlockContextualToolbar from './block-contextual-toolbar';
import { store as blockEditorStore } from '../../store';
import BlockPopover from '../block-popover';
Expand Down Expand Up @@ -45,7 +44,6 @@ function selector( select ) {

function SelectedBlockPopover( {
clientId,
rootClientId,
isEmptyDefaultBlock,
showContents, // we may need to mount an empty popover because we reuse
capturingClientId,
Expand Down Expand Up @@ -139,6 +137,8 @@ function SelectedBlockPopover( {
__unstableContentRef={ __unstableContentRef }
resize={ false }
{ ...popoverProps }
role="region"
tabIndex="-1"
>
{ shouldShowContextualToolbar && showContents && (
<BlockContextualToolbar
Expand All @@ -156,12 +156,6 @@ function SelectedBlockPopover( {
key={ clientId }
/>
) }
{ shouldShowBreadcrumb && (
<BlockSelectionButton
clientId={ clientId }
rootClientId={ rootClientId }
/>
) }
</BlockPopover>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export function useWritingFlow() {
( node ) => {
node.tabIndex = -1;
node.contentEditable = hasMultiSelection;
node.role = 'region';

if ( ! hasMultiSelection ) {
return;
Expand Down
Loading