diff --git a/packages/block-editor/src/components/block-settings-menu/block-settings-dropdown.js b/packages/block-editor/src/components/block-settings-menu/block-settings-dropdown.js index ce059d7720a9ff..5f844b2a03668a 100644 --- a/packages/block-editor/src/components/block-settings-menu/block-settings-dropdown.js +++ b/packages/block-editor/src/components/block-settings-menu/block-settings-dropdown.js @@ -18,7 +18,7 @@ import { import { __, sprintf } from '@wordpress/i18n'; import { store as keyboardShortcutsStore, - __unstableUseShortcutEventMatch, + useShortcut, } from '@wordpress/keyboard-shortcuts'; import { pipe, useCopyToClipboard } from '@wordpress/compose'; @@ -47,12 +47,51 @@ function CopyMenuItem( { blocks, onCopy, label } ) { } export function BlockSettingsDropdown( { + clientIds, + __experimentalSelectBlock, + ...props +} ) { + return ( + + { ( blocksActionsProps ) => ( + + ) } + + ); +} + +function BlockSettingsDropdownMenu( { clientIds, __experimentalSelectBlock, children, __unstableDisplayLocation, + + // The following props are injected by BlockActions + canCopyStyles, + canDuplicate, + canInsertDefaultBlock, + canMove, + canRemove, + onDuplicate, + onInsertAfter, + onInsertBefore, + onRemove, + onCopy, + onPasteStyles, + onMoveTo, + blocks, + ...props } ) { + const scope = useRef(); const blockClientIds = Array.isArray( clientIds ) ? clientIds : [ clientIds ]; @@ -117,7 +156,6 @@ export function BlockSettingsDropdown( { ), }; }, [] ); - const isMatch = __unstableUseShortcutEventMatch(); const { selectBlock } = useDispatch( blockEditorStore ); const hasSelectedBlocks = selectedBlockClientIds.length > 0; @@ -158,6 +196,50 @@ export function BlockSettingsDropdown( { getSelectedBlockClientIds, ] ); + useShortcut( + 'core/block-editor/remove', + ( event ) => { + if ( event.defaultPrevented || ! canRemove ) return; + + event.preventDefault(); + updateSelectionAfterRemove( onRemove() ); + }, + { scope } + ); + + useShortcut( + 'core/block-editor/duplicate', + ( event ) => { + if ( event.defaultPrevented || ! canDuplicate ) return; + + event.preventDefault(); + updateSelectionAfterDuplicate( onDuplicate() ); + }, + { scope } + ); + + useShortcut( + 'core/block-editor/insert-after', + ( event ) => { + if ( event.defaultPrevented || ! canInsertDefaultBlock ) return; + + event.preventDefault(); + onInsertAfter(); + }, + { scope } + ); + + useShortcut( + 'core/block-editor/insert-after', + ( event ) => { + if ( event.defaultPrevented || ! canInsertDefaultBlock ) return; + + event.preventDefault(); + onInsertBefore(); + }, + { scope } + ); + const removeBlockLabel = count === 1 ? __( 'Delete' ) : __( 'Delete blocks' ); @@ -175,203 +257,120 @@ export function BlockSettingsDropdown( { selectedBlockClientIds?.includes( firstParentClientId ); return ( - - { ( { - canCopyStyles, - canDuplicate, - canInsertDefaultBlock, - canMove, - canRemove, - onDuplicate, - onInsertAfter, - onInsertBefore, - onRemove, - onCopy, - onPasteStyles, - onMoveTo, - blocks, - } ) => ( - - { ( { onClose } ) => ( - <> - - <__unstableBlockSettingsMenuFirstItem.Slot - fillProps={ { onClose } } - /> - { ! parentBlockIsSelected && - !! firstParentClientId && ( - - } - onClick={ () => - selectBlock( - firstParentClientId - ) - } - > - { sprintf( - /* translators: %s: Name of the block's parent. */ - __( - 'Select parent block (%s)' - ), - parentBlockType.title - ) } - - ) } - { count === 1 && ( - - ) } - - { canDuplicate && ( - - { __( 'Duplicate' ) } - - ) } - { canInsertDefaultBlock && ( - <> - - { __( 'Add before' ) } - - - { __( 'Add after' ) } - - + { ( { onClose } ) => ( + <> + + <__unstableBlockSettingsMenuFirstItem.Slot + fillProps={ { onClose } } + /> + { ! parentBlockIsSelected && !! firstParentClientId && ( + + } + onClick={ () => + selectBlock( firstParentClientId ) + } + > + { sprintf( + /* translators: %s: Name of the block's parent. */ + __( 'Select parent block (%s)' ), + parentBlockType.title ) } - - { canCopyStyles && ( - - - - { __( 'Paste styles' ) } - - - ) } - + ) } + { count === 1 && ( + + ) } + + { canDuplicate && ( + + { __( 'Duplicate' ) } + + ) } + { canInsertDefaultBlock && ( + <> + + { __( 'Add before' ) } + + + { __( 'Add after' ) } + + + ) } + + { canCopyStyles && ( + + - { typeof children === 'function' - ? children( { onClose } ) - : Children.map( ( child ) => - cloneElement( child, { onClose } ) - ) } - { canRemove && ( - - - { removeBlockLabel } - - - ) } - + + { __( 'Paste styles' ) } + + + ) } + + { typeof children === 'function' + ? children( { onClose } ) + : Children.map( ( child ) => + cloneElement( child, { onClose } ) + ) } + { canRemove && ( + + + { removeBlockLabel } + + ) } - + ) } - + ); } diff --git a/packages/block-editor/src/components/block-tools/index.js b/packages/block-editor/src/components/block-tools/index.js index 8e3b240838fd04..8b3105ac85f6a0 100644 --- a/packages/block-editor/src/components/block-tools/index.js +++ b/packages/block-editor/src/components/block-tools/index.js @@ -4,7 +4,7 @@ import { useSelect, useDispatch } from '@wordpress/data'; import { useViewportMatch } from '@wordpress/compose'; import { Popover } from '@wordpress/components'; -import { __unstableUseShortcutEventMatch as useShortcutEventMatch } from '@wordpress/keyboard-shortcuts'; +import { useShortcut } from '@wordpress/keyboard-shortcuts'; import { useRef } from '@wordpress/element'; /** @@ -45,12 +45,12 @@ export default function BlockTools( { __unstableContentRef, ...props } ) { + const scope = useRef( null ); const isLargeViewport = useViewportMatch( 'medium' ); const { hasFixedToolbar, isZoomOutMode, isTyping } = useSelect( selector, [] ); - const isMatch = useShortcutEventMatch(); const { getSelectedBlockClientIds, getBlockRootClientId } = useSelect( blockEditorStore ); const { @@ -64,74 +64,89 @@ export default function BlockTools( { moveBlocksDown, } = useDispatch( blockEditorStore ); - function onKeyDown( event ) { - if ( event.defaultPrevented ) return; - - if ( isMatch( 'core/block-editor/move-up', event ) ) { + useShortcut( + 'core/block-editor/move-up', + ( event ) => { + if ( event.defaultPrevented ) return; const clientIds = getSelectedBlockClientIds(); if ( clientIds.length ) { event.preventDefault(); const rootClientId = getBlockRootClientId( clientIds[ 0 ] ); moveBlocksUp( clientIds, rootClientId ); } - } else if ( isMatch( 'core/block-editor/move-down', event ) ) { - const clientIds = getSelectedBlockClientIds(); - if ( clientIds.length ) { - event.preventDefault(); - const rootClientId = getBlockRootClientId( clientIds[ 0 ] ); - moveBlocksDown( clientIds, rootClientId ); - } - } else if ( isMatch( 'core/block-editor/duplicate', event ) ) { - const clientIds = getSelectedBlockClientIds(); - if ( clientIds.length ) { - event.preventDefault(); - duplicateBlocks( clientIds ); - } - } else if ( isMatch( 'core/block-editor/remove', event ) ) { - const clientIds = getSelectedBlockClientIds(); - if ( clientIds.length ) { - event.preventDefault(); - removeBlocks( clientIds ); - } - } else if ( isMatch( 'core/block-editor/insert-after', event ) ) { - const clientIds = getSelectedBlockClientIds(); - if ( clientIds.length ) { - event.preventDefault(); - insertAfterBlock( clientIds[ clientIds.length - 1 ] ); - } - } else if ( isMatch( 'core/block-editor/insert-before', event ) ) { - const clientIds = getSelectedBlockClientIds(); - if ( clientIds.length ) { - event.preventDefault(); - insertBeforeBlock( clientIds[ 0 ] ); - } - } else if ( isMatch( 'core/block-editor/unselect', event ) ) { - const clientIds = getSelectedBlockClientIds(); - if ( clientIds.length ) { - event.preventDefault(); + }, + { scope } + ); + + useShortcut( 'core/block-editor/move-down', ( event ) => { + if ( event.defaultPrevented ) return; + const clientIds = getSelectedBlockClientIds(); + if ( clientIds.length ) { + event.preventDefault(); + const rootClientId = getBlockRootClientId( clientIds[ 0 ] ); + moveBlocksDown( clientIds, rootClientId ); + } + } ); + + useShortcut( 'core/block-editor/duplicate', ( event ) => { + if ( event.defaultPrevented ) return; + const clientIds = getSelectedBlockClientIds(); + if ( clientIds.length ) { + event.preventDefault(); + duplicateBlocks( clientIds ); + } + } ); + + useShortcut( 'core/block-editor/remove', ( event ) => { + const clientIds = getSelectedBlockClientIds(); + if ( clientIds.length ) { + event.preventDefault(); + removeBlocks( clientIds ); + } + } ); + + useShortcut( 'core/block-editor/insert-after', ( event ) => { + const clientIds = getSelectedBlockClientIds(); + if ( clientIds.length ) { + event.preventDefault(); + insertAfterBlock( clientIds[ clientIds.length - 1 ] ); + } + } ); + + useShortcut( 'core/block-editor/insert-before', ( event ) => { + const clientIds = getSelectedBlockClientIds(); + if ( clientIds.length ) { + event.preventDefault(); + insertBeforeBlock( clientIds[ 0 ] ); + } + } ); + + useShortcut( 'core/block-editor/unselect', ( event ) => { + const clientIds = getSelectedBlockClientIds(); + if ( clientIds.length ) { + event.preventDefault(); - // If there is more than one block selected, select the first - // block so that focus is directed back to the beginning of the selection. - // In effect, to the user this feels like deselecting the multi-selection. - if ( clientIds.length > 1 ) { - selectBlock( clientIds[ 0 ] ); - } else { - clearSelectedBlock(); - } - event.target.ownerDocument.defaultView - .getSelection() - .removeAllRanges(); - __unstableContentRef?.current.focus(); + // If there is more than one block selected, select the first + // block so that focus is directed back to the beginning of the selection. + // In effect, to the user this feels like deselecting the multi-selection. + if ( clientIds.length > 1 ) { + selectBlock( clientIds[ 0 ] ); + } else { + clearSelectedBlock(); } + event.target.ownerDocument.defaultView + .getSelection() + .removeAllRanges(); + __unstableContentRef?.current.focus(); } - } + } ); const blockToolbarRef = usePopoverScroll( __unstableContentRef ); const blockToolbarAfterRef = usePopoverScroll( __unstableContentRef ); return ( // eslint-disable-next-line jsx-a11y/no-static-element-interactions -
+
{ ! isTyping && ( 0 && - getSelectedBlockClientIds().length === 0; + // Update the selection if the original selection has been removed. + const shouldUpdateSelection = + selectedBlockClientIds.length > 0 && + getSelectedBlockClientIds().length === 0; - // If there's no previous block nor parent block, focus the first block. - if ( ! blockToFocus ) { - blockToFocus = getBlockOrder()[ 0 ]; - } + // If there's no previous block nor parent block, focus the first block. + if ( ! blockToFocus ) { + blockToFocus = getBlockOrder()[ 0 ]; + } + + updateFocusAndSelection( blockToFocus, shouldUpdateSelection ); + } - updateFocusAndSelection( blockToFocus, shouldUpdateSelection ); - } else if ( isMatch( 'core/block-editor/duplicate', event ) ) { + useShortcut( 'core/block-editor/remove', onRemoveBlock, { scope } ); + useShortcut( + 'core/block-editor/duplicate', + async ( event ) => { if ( event.defaultPrevented ) { return; } @@ -183,9 +179,22 @@ function ListViewBlockSelectButton( updateFocusAndSelection( updatedBlocks[ 0 ], false ); } } + }, + { scope } + ); + + async function onKeyDownHandler( event ) { + if ( event.keyCode === ENTER || event.keyCode === SPACE ) { + onClick( event ); + } else if ( event.keyCode === BACKSPACE || event.keyCode === DELETE ) { + onRemoveBlock(); } } + const scopeRef = useRefEffect( ( node ) => { + scope.current = node; + }, [] ); + return ( <>