Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
List View: Unify shortcut handlers
  • Loading branch information
Mamaduka committed Apr 26, 2024
commit 364e27a652820f2de3b76ce361275653b4ac13ae
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,7 @@ import { moreVertical } from '@wordpress/icons';
import { Children, cloneElement } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { displayShortcut } from '@wordpress/keycodes';
import {
store as keyboardShortcutsStore,
__unstableUseShortcutEventMatch,
} from '@wordpress/keyboard-shortcuts';
import { store as keyboardShortcutsStore } from '@wordpress/keyboard-shortcuts';
import { pipe, useCopyToClipboard } from '@wordpress/compose';

/**
Expand Down Expand Up @@ -125,7 +122,6 @@ export function BlockSettingsDropdown( {
),
};
}, [] );
const isMatch = __unstableUseShortcutEventMatch();
const hasSelectedBlocks = selectedBlockClientIds.length > 0;

async function updateSelectionAfterDuplicate( clientIdsPromise ) {
Expand Down Expand Up @@ -213,52 +209,6 @@ export function BlockSettingsDropdown( {
open={ open }
onToggle={ onToggle }
noIcons
menuProps={ {
/**
* @param {KeyboardEvent} event
*/
onKeyDown( event ) {
if ( event.defaultPrevented ) return;

if (
isMatch( 'core/block-editor/remove', event ) &&
canRemove
) {
event.preventDefault();
onRemove();
updateSelectionAfterRemove();
} else if (
isMatch(
'core/block-editor/duplicate',
event
) &&
canDuplicate
) {
event.preventDefault();
updateSelectionAfterDuplicate( onDuplicate() );
} else if (
isMatch(
'core/block-editor/insert-after',
event
) &&
canInsertBlock
) {
event.preventDefault();
setOpenedBlockSettingsMenu( undefined );
onInsertAfter();
} else if (
isMatch(
'core/block-editor/insert-before',
event
) &&
canInsertBlock
) {
event.preventDefault();
setOpenedBlockSettingsMenu( undefined );
onInsertBefore();
}
},
} }
{ ...props }
>
{ ( { onClose } ) => (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import classnames from 'classnames';
/**
* WordPress dependencies
*/
import { hasBlockSupport } from '@wordpress/blocks';
import {
Button,
__experimentalHStack as HStack,
Expand All @@ -15,11 +14,7 @@ import {
} from '@wordpress/components';
import { forwardRef } from '@wordpress/element';
import { Icon, lockSmall as lock, pinSmall } from '@wordpress/icons';
import { SPACE, ENTER, BACKSPACE, DELETE } from '@wordpress/keycodes';
import { useSelect, useDispatch } from '@wordpress/data';
import { __unstableUseShortcutEventMatch as useShortcutEventMatch } from '@wordpress/keyboard-shortcuts';
import { __, sprintf } from '@wordpress/i18n';
import isShallowEqual from '@wordpress/is-shallow-equal';

/**
* Internal dependencies
Expand All @@ -29,9 +24,7 @@ import useBlockDisplayInformation from '../use-block-display-information';
import useBlockDisplayTitle from '../block-title/use-block-display-title';
import ListViewExpander from './expander';
import { useBlockLock } from '../block-lock';
import { store as blockEditorStore } from '../../store';
import useListViewImages from './use-list-view-images';
import { useListViewContext } from './context';

function ListViewBlockSelectButton(
{
Expand All @@ -48,7 +41,6 @@ function ListViewBlockSelectButton(
draggable,
isExpanded,
ariaDescribedBy,
updateFocusAndSelection,
},
ref
) {
Expand All @@ -58,27 +50,8 @@ function ListViewBlockSelectButton(
context: 'list-view',
} );
const { isLocked } = useBlockLock( clientId );
const {
canInsertBlockType,
getSelectedBlockClientIds,
getPreviousBlockClientId,
getBlockRootClientId,
getBlockOrder,
getBlockParents,
getBlocksByClientId,
canRemoveBlocks,
} = useSelect( blockEditorStore );
const {
duplicateBlocks,
multiSelect,
removeBlocks,
insertAfterBlock,
insertBeforeBlock,
} = useDispatch( blockEditorStore );
const isMatch = useShortcutEventMatch();
const isSticky = blockInformation?.positionType === 'sticky';
const images = useListViewImages( { clientId, isExpanded } );
const { collapseAll, expand, rootClientId } = useListViewContext();

const positionLabel = blockInformation?.positionLabel
? sprintf(
Expand All @@ -97,180 +70,6 @@ function ListViewBlockSelectButton(
onDragStart?.( event );
};

// Determine which blocks to update:
// If the current (focused) block is part of the block selection, use the whole selection.
// If the focused block is not part of the block selection, only update the focused block.
function getBlocksToUpdate() {
const selectedBlockClientIds = getSelectedBlockClientIds();
const isUpdatingSelectedBlocks =
selectedBlockClientIds.includes( clientId );
const firstBlockClientId = isUpdatingSelectedBlocks
? selectedBlockClientIds[ 0 ]
: clientId;
const firstBlockRootClientId =
getBlockRootClientId( firstBlockClientId );

const blocksToUpdate = isUpdatingSelectedBlocks
? selectedBlockClientIds
: [ clientId ];

return {
blocksToUpdate,
firstBlockClientId,
firstBlockRootClientId,
selectedBlockClientIds,
};
}

/**
* @param {KeyboardEvent} event
*/
async function onKeyDownHandler( event ) {
if ( event.keyCode === ENTER || event.keyCode === SPACE ) {
onClick( event );
} else if (
event.keyCode === BACKSPACE ||
event.keyCode === DELETE ||
isMatch( 'core/block-editor/remove', event )
) {
const {
blocksToUpdate: blocksToDelete,
firstBlockClientId,
firstBlockRootClientId,
selectedBlockClientIds,
} = getBlocksToUpdate();

// Don't update the selection if the blocks cannot be deleted.
if ( ! canRemoveBlocks( blocksToDelete, firstBlockRootClientId ) ) {
return;
}

let blockToFocus =
getPreviousBlockClientId( firstBlockClientId ) ??
// If the previous block is not found (when the first block is deleted),
// fallback to focus the parent block.
firstBlockRootClientId;

removeBlocks( blocksToDelete, false );

// 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 ];
}

updateFocusAndSelection( blockToFocus, shouldUpdateSelection );
} else if ( isMatch( 'core/block-editor/duplicate', event ) ) {
if ( event.defaultPrevented ) {
return;
}
event.preventDefault();

const { blocksToUpdate, firstBlockRootClientId } =
getBlocksToUpdate();

const canDuplicate = getBlocksByClientId( blocksToUpdate ).every(
( block ) => {
return (
!! block &&
hasBlockSupport( block.name, 'multiple', true ) &&
canInsertBlockType( block.name, firstBlockRootClientId )
);
}
);

if ( canDuplicate ) {
const updatedBlocks = await duplicateBlocks(
blocksToUpdate,
false
);

if ( updatedBlocks?.length ) {
// If blocks have been duplicated, focus the first duplicated block.
updateFocusAndSelection( updatedBlocks[ 0 ], false );
}
}
} else if ( isMatch( 'core/block-editor/insert-before', event ) ) {
if ( event.defaultPrevented ) {
return;
}
event.preventDefault();

const { blocksToUpdate } = getBlocksToUpdate();
await insertBeforeBlock( blocksToUpdate[ 0 ] );
const newlySelectedBlocks = getSelectedBlockClientIds();

// Focus the first block of the newly inserted blocks, to keep focus within the list view.
updateFocusAndSelection( newlySelectedBlocks[ 0 ], false );
} else if ( isMatch( 'core/block-editor/insert-after', event ) ) {
if ( event.defaultPrevented ) {
return;
}
event.preventDefault();

const { blocksToUpdate } = getBlocksToUpdate();
await insertAfterBlock( blocksToUpdate.at( -1 ) );
const newlySelectedBlocks = getSelectedBlockClientIds();

// Focus the first block of the newly inserted blocks, to keep focus within the list view.
updateFocusAndSelection( newlySelectedBlocks[ 0 ], false );
} else if ( isMatch( 'core/block-editor/select-all', event ) ) {
if ( event.defaultPrevented ) {
return;
}
event.preventDefault();

const { firstBlockRootClientId, selectedBlockClientIds } =
getBlocksToUpdate();
const blockClientIds = getBlockOrder( firstBlockRootClientId );
if ( ! blockClientIds.length ) {
return;
}

// If we have selected all sibling nested blocks, try selecting up a level.
// This is a similar implementation to that used by `useSelectAll`.
// `isShallowEqual` is used for the list view instead of a length check,
// as the array of siblings of the currently focused block may be a different
// set of blocks from the current block selection if the user is focused
// on a different part of the list view from the block selection.
if ( isShallowEqual( selectedBlockClientIds, blockClientIds ) ) {
// Only select up a level if the first block is not the root block.
// This ensures that the block selection can't break out of the root block
// used by the list view, if the list view is only showing a partial hierarchy.
if (
firstBlockRootClientId &&
firstBlockRootClientId !== rootClientId
) {
updateFocusAndSelection( firstBlockRootClientId, true );
return;
}
}

// Select all while passing `null` to skip focusing to the editor canvas,
// and retain focus within the list view.
multiSelect(
blockClientIds[ 0 ],
blockClientIds[ blockClientIds.length - 1 ],
null
);
} else if ( isMatch( 'core/block-editor/collapse-list-view', event ) ) {
if ( event.defaultPrevented ) {
return;
}
event.preventDefault();
const { firstBlockClientId } = getBlocksToUpdate();
const blockParents = getBlockParents( firstBlockClientId, false );
// Collapse all blocks.
collapseAll();
// Expand all parents of the current block.
expand( blockParents );
}
}

return (
<>
<Button
Expand All @@ -280,7 +79,6 @@ function ListViewBlockSelectButton(
) }
onClick={ onClick }
onContextMenu={ onContextMenu }
onKeyDown={ onKeyDownHandler }
onMouseDown={ onMouseDown }
ref={ ref }
tabIndex={ tabIndex }
Expand Down
Loading