diff --git a/packages/block-editor/src/components/list-view/index.js b/packages/block-editor/src/components/list-view/index.js index 9985727a7b451..f1c8f982d4361 100644 --- a/packages/block-editor/src/components/list-view/index.js +++ b/packages/block-editor/src/components/list-view/index.js @@ -24,6 +24,7 @@ import ListViewDropIndicator from './drop-indicator'; import useListViewClientIds from './use-list-view-client-ids'; import useListViewDropZone from './use-list-view-drop-zone'; import { store as blockEditorStore } from '../../store'; +import { hasFocusWithin } from './utils'; const noop = () => {}; const expanded = ( state, action ) => { @@ -66,11 +67,14 @@ function ListView( clientIdsTree, selectedClientIds, draggedClientIds, + selectedBlockRootClientId, + selectedBlockParentIds, } = useListViewClientIds( blocks, showOnlyCurrentHierarchy, __experimentalPersistentListViewFeatures ); + const { selectBlock } = useDispatch( blockEditorStore ); const selectEditorBlock = useCallback( ( clientId ) => { @@ -80,12 +84,12 @@ function ListView( [ selectBlock, onSelect ] ); const [ expandedState, setExpandedState ] = useReducer( expanded, {} ); - const { ref: dropZoneRef, target: blockDropTarget } = useListViewDropZone(); const elementRef = useRef(); const treeGridRef = useMergeRefs( [ elementRef, dropZoneRef, ref ] ); - const isMounted = useRef( false ); + const hasFocus = hasFocusWithin( elementRef?.current ); + useEffect( () => { isMounted.current = true; }, [] ); @@ -125,6 +129,7 @@ function ListView( expandedState, expand, collapse, + selectedBlockRootClientId, } ), [ __experimentalFeatures, @@ -135,9 +140,30 @@ function ListView( expandedState, expand, collapse, + selectedBlockRootClientId, ] ); + // If a selection is made outside the block list, + // for example, in the Block Editor, + // try to expand the block list tree. + useEffect( () => { + if ( + ! hasFocus && + Array.isArray( selectedBlockParentIds ) && + selectedBlockParentIds.length + ) { + selectedBlockParentIds.forEach( ( clientId ) => { + if ( ! expandedState[ clientId ] ) { + setExpandedState( { + type: 'expand', + clientId, + } ); + } + } ); + } + }, [ hasFocus, selectedBlockParentIds ] ); + return ( { const { getSelectedBlockClientId, getSelectedBlockClientIds, getDraggedBlockClientIds, + getBlockParents, } = select( blockEditorStore ); if ( __experimentalPersistentListViewFeatures ) { + const selectedBlockClientIds = getSelectedBlockClientIds(); return { - selectedClientIds: getSelectedBlockClientIds(), + selectedClientIds: selectedBlockClientIds, draggedClientIds: getDraggedBlockClientIds(), + selectedBlockParentIds: getBlockParents( + selectedBlockClientIds[ 0 ], + false + ), }; } + const selectedBlockClientId = getSelectedBlockClientId(); return { - selectedClientIds: getSelectedBlockClientId(), + selectedClientIds: selectedBlockClientId, draggedClientIds: getDraggedBlockClientIds(), + selectedBlockParentIds: getBlockParents( + selectedBlockClientId, + false + ), }; }, [ __experimentalPersistentListViewFeatures ] @@ -84,5 +99,11 @@ export default function useListViewClientIds( selectedClientIds, showOnlyCurrentHierarchy ); - return { clientIdsTree, selectedClientIds, draggedClientIds }; + + return { + clientIdsTree, + selectedClientIds, + draggedClientIds, + selectedBlockParentIds, + }; } diff --git a/packages/block-editor/src/components/list-view/utils.js b/packages/block-editor/src/components/list-view/utils.js index 50bb561e42a8a..b103eb4664314 100644 --- a/packages/block-editor/src/components/list-view/utils.js +++ b/packages/block-editor/src/components/list-view/utils.js @@ -30,3 +30,14 @@ export const isClientIdSelected = ( clientId, selectedBlockClientIds ) => isArray( selectedBlockClientIds ) && selectedBlockClientIds.length ? selectedBlockClientIds.indexOf( clientId ) !== -1 : selectedBlockClientIds === clientId; + +/** + * Returns true if the container contains the document active element. + * + * @param {HTMLElement} container An HTML element. + * + * @return {boolean} Whether the container contains the currently document active element. + */ +export const hasFocusWithin = ( container ) => { + return !! container?.contains( container?.ownerDocument?.activeElement ); +};