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: Fix performance issue when selecting all blocks #54900

Merged
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
46 changes: 3 additions & 43 deletions packages/block-editor/src/components/list-view/block.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,9 @@ import {
} from '@wordpress/components';
import { useInstanceId } from '@wordpress/compose';
import { moreVertical } from '@wordpress/icons';
import {
useState,
useRef,
useEffect,
useCallback,
memo,
} from '@wordpress/element';
import { useState, useRef, useCallback, memo } from '@wordpress/element';
import { useDispatch, useSelect } from '@wordpress/data';
import { sprintf, __ } from '@wordpress/i18n';
import { focus } from '@wordpress/dom';
import { ESCAPE } from '@wordpress/keycodes';

/**
Expand All @@ -36,7 +29,7 @@ import {
} from '../block-mover/button';
import ListViewBlockContents from './block-contents';
import { useListViewContext } from './context';
import { getBlockPositionDescription } from './utils';
import { getBlockPositionDescription, focusListItem } from './utils';
import { store as blockEditorStore } from '../../store';
import useBlockDisplayInformation from '../use-block-display-information';
import { useBlockLock } from '../block-lock';
Expand Down Expand Up @@ -120,7 +113,6 @@ function ListViewBlock( {
);

const {
isTreeGridMounted,
expand,
collapse,
BlockSettingsMenu,
Expand All @@ -142,15 +134,6 @@ function ListViewBlock( {
{ 'is-visible': isHovered || isFirstSelectedBlock }
);

// If ListView has experimental features related to the Persistent List View,
// only focus the selected list item on mount; otherwise the list would always
// try to steal the focus from the editor canvas.
useEffect( () => {
if ( ! isTreeGridMounted && isSelected ) {
cellRef.current.focus();
}
}, [] );

// If multiple blocks are selected, deselect all blocks when the user
// presses the escape key.
const onKeyDown = ( event ) => {
Expand Down Expand Up @@ -188,30 +171,7 @@ function ListViewBlock( {
selectBlock( undefined, focusClientId, null, null );
}

const getFocusElement = () => {
const row = treeGridElementRef.current?.querySelector(
`[role=row][data-block="${ focusClientId }"]`
);
if ( ! row ) return null;
// Focus the first focusable in the row, which is the ListViewBlockSelectButton.
return focus.focusable.find( row )[ 0 ];
};

let focusElement = getFocusElement();
if ( focusElement ) {
focusElement.focus();
} else {
// The element hasn't been painted yet. Defer focusing on the next frame.
// This could happen when all blocks have been deleted and the default block
// hasn't been added to the editor yet.
window.requestAnimationFrame( () => {
focusElement = getFocusElement();
// Ignore if the element still doesn't exist.
if ( focusElement ) {
focusElement.focus();
}
} );
}
focusListItem( focusClientId, treeGridElementRef );
},
[ selectBlock, treeGridElementRef ]
);
Expand Down
12 changes: 11 additions & 1 deletion packages/block-editor/src/components/list-view/branch.js
Original file line number Diff line number Diff line change
Expand Up @@ -168,8 +168,18 @@ function ListViewBranch( props ) {
);
const isSelectedBranch =
isBranchSelected || ( isSelected && hasNestedBlocks );

// To avoid performance issues, we only render blocks that are in view,
// or blocks that are selected or dragged. If a block is selected,
// it is only counted if it is the first of the block selection.
// This prevents the entire tree from being rendered when a branch is
// selected, or a user selects all blocks, while still enabling scroll
// into view behavior when selecting a block or opening the list view.
const showBlock =
isDragged || blockInView || isSelected || isBranchDragged;
isDragged ||
blockInView ||
isBranchDragged ||
( isSelected && clientId === selectedClientIds[ 0 ] );
return (
<AsyncModeProvider key={ clientId } value={ ! isSelected }>
{ showBlock && (
Expand Down
12 changes: 8 additions & 4 deletions packages/block-editor/src/components/list-view/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import useListViewDropZone from './use-list-view-drop-zone';
import useListViewExpandSelectedItem from './use-list-view-expand-selected-item';
import { store as blockEditorStore } from '../../store';
import { BlockSettingsDropdown } from '../block-settings-menu/block-settings-dropdown';
import { focusListItem } from './utils';

const expanded = ( state, action ) => {
if ( Array.isArray( action.clientIds ) ) {
Expand Down Expand Up @@ -132,8 +133,6 @@ function ListViewComponent(
const elementRef = useRef();
const treeGridRef = useMergeRefs( [ elementRef, dropZoneRef, ref ] );

const isMounted = useRef( false );

const [ insertedBlock, setInsertedBlock ] = useState( null );

const { setSelectedTreeId } = useListViewExpandSelectedItem( {
Expand All @@ -156,7 +155,13 @@ function ListViewComponent(
[ setSelectedTreeId, updateBlockSelection, onSelect, getBlock ]
);
useEffect( () => {
isMounted.current = true;
// If a blocks are already selected when the list view is initially
// mounted, shift focus to the first selected block.
if ( selectedClientIds?.length ) {
focusListItem( selectedClientIds[ 0 ], elementRef );
}
// Disable reason: Only focus on the selected item when the list view is mounted.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [] );

const expand = useCallback(
Expand Down Expand Up @@ -204,7 +209,6 @@ function ListViewComponent(

const contextValue = useMemo(
() => ( {
isTreeGridMounted: isMounted.current,
draggedClientIds,
expandedState,
expand,
Expand Down
37 changes: 37 additions & 0 deletions packages/block-editor/src/components/list-view/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* WordPress dependencies
*/
import { __, sprintf } from '@wordpress/i18n';
import { focus } from '@wordpress/dom';

export const getBlockPositionDescription = ( position, siblingCount, level ) =>
sprintf(
Expand Down Expand Up @@ -56,3 +57,39 @@ export function getCommonDepthClientIds(
end,
};
}

/**
* Shift focus to the list view item associated with a particular clientId.
*
* @typedef {import('@wordpress/element').RefObject} RefObject
*
* @param {string} focusClientId The client ID of the block to focus.
* @param {RefObject<HTMLElement>} treeGridElementRef The container element to search within.
*/
export function focusListItem( focusClientId, treeGridElementRef ) {
const getFocusElement = () => {
const row = treeGridElementRef.current?.querySelector(
`[role=row][data-block="${ focusClientId }"]`
);
if ( ! row ) return null;
// Focus the first focusable in the row, which is the ListViewBlockSelectButton.
return focus.focusable.find( row )[ 0 ];
};

let focusElement = getFocusElement();
if ( focusElement ) {
focusElement.focus();
} else {
// The element hasn't been painted yet. Defer focusing on the next frame.
// This could happen when all blocks have been deleted and the default block
// hasn't been added to the editor yet.
window.requestAnimationFrame( () => {
focusElement = getFocusElement();

// Ignore if the element still doesn't exist.
if ( focusElement ) {
focusElement.focus();
}
} );
}
}