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

List View: Unify shortcut handlers #61130

Merged
merged 4 commits into from
Apr 30, 2024
Merged
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 @@ -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,8 @@ 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 { SPACE, ENTER } from '@wordpress/keycodes';
import { __, sprintf } from '@wordpress/i18n';
import isShallowEqual from '@wordpress/is-shallow-equal';

/**
* Internal dependencies
Expand All @@ -29,9 +25,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 +42,6 @@ function ListViewBlockSelectButton(
draggable,
isExpanded,
ariaDescribedBy,
updateFocusAndSelection,
},
ref
) {
Expand All @@ -58,27 +51,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,190 +71,24 @@ 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 ) {
function onKeyDown( 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
className={ classnames(
'block-editor-list-view-block-select-button',
className
) }
onClick={ onClick }
onContextMenu={ onContextMenu }
onKeyDown={ onKeyDownHandler }
onKeyDown={ onKeyDown }
onMouseDown={ onMouseDown }
ref={ ref }
tabIndex={ tabIndex }
Expand Down Expand Up @@ -346,7 +154,6 @@ function ListViewBlockSelectButton(
) }
</HStack>
</Button>
</>
);
}

Expand Down
Loading
Loading