diff --git a/packages/edit-site/src/components/navigation-inspector/index.js b/packages/edit-site/src/components/navigation-inspector/index.js
index 0034362581ddd..e25cb30d7b180 100644
--- a/packages/edit-site/src/components/navigation-inspector/index.js
+++ b/packages/edit-site/src/components/navigation-inspector/index.js
@@ -2,13 +2,11 @@
* WordPress dependencies
*/
import { useSelect } from '@wordpress/data';
-import { useEffect } from '@wordpress/element';
+import { useState, useEffect } from '@wordpress/element';
import { store as coreStore, useEntityBlockEditor } from '@wordpress/core-data';
import {
store as blockEditorStore,
BlockEditorProvider,
- BlockTools,
- BlockList,
} from '@wordpress/block-editor';
import { speak } from '@wordpress/a11y';
import { __ } from '@wordpress/i18n';
@@ -18,91 +16,130 @@ import { __ } from '@wordpress/i18n';
*/
import NavigationMenu from './navigation-menu';
-const NAVIGATION_MENUS_QUERY = [
- 'postType',
- 'wp_navigation',
- [ { per_page: 1, status: 'publish' } ],
-];
-
-function NavigationBlockEditorLoader( { onSelect, navigationPostId } ) {
- const [ innerBlocks, onInput, onChange ] = useEntityBlockEditor(
- 'postType',
- 'wp_navigation',
- { id: navigationPostId }
- );
- return (
-
-
-
-
-
-
-
-
- );
-}
+const NAVIGATION_MENUS_QUERY = [ { per_page: -1, status: 'publish' } ];
export default function NavigationInspector( { onSelect } ) {
const {
- navigationBlockId,
- navigationPostId,
- isLoadingInnerBlocks,
- hasLoadedInnerBlocks,
+ selectedNavigationBlockId,
+ clientIdToRef,
+ navigationMenus,
+ isResolvingNavigationMenus,
+ hasResolvedNavigationMenus,
+ firstNavigationBlockId,
} = useSelect( ( select ) => {
const {
__experimentalGetActiveBlockIdByBlockNames,
__experimentalGetGlobalBlocksByName,
getBlock,
} = select( blockEditorStore );
- const { isResolving, hasFinishedResolution, getEntityRecords } =
+
+ const { getEntityRecords, hasFinishedResolution, isResolving } =
select( coreStore );
- const selectedNavBlockId =
- __experimentalGetActiveBlockIdByBlockNames( 'core/navigation' ) ||
- __experimentalGetGlobalBlocksByName( 'core/navigation' )?.[ 0 ];
-
- let navigationPost, isLoading, hasLoaded;
- if ( selectedNavBlockId ) {
- navigationPost = getBlock( selectedNavBlockId )?.attributes?.ref;
- isLoading = isResolving( 'getEntityRecord', [
- 'postType',
- 'wp_navigation',
- navigationPost,
- ] );
- hasLoaded = hasFinishedResolution( 'getEntityRecord', [
- 'postType',
- 'wp_navigation',
- navigationPost,
- ] );
- } else {
- const navigationMenus = getEntityRecords(
- ...NAVIGATION_MENUS_QUERY
- );
- if ( navigationMenus?.length > 0 ) {
- navigationPost = navigationMenus[ 0 ].id;
- }
- isLoading = isResolving(
- 'getEntityRecords',
- NAVIGATION_MENUS_QUERY
- );
- hasLoaded = hasFinishedResolution(
- 'getEntityRecords',
- NAVIGATION_MENUS_QUERY
- );
- }
+ const navigationMenusQuery = [
+ 'postType',
+ 'wp_navigation',
+ NAVIGATION_MENUS_QUERY[ 0 ],
+ ];
+
+ // Get the active Navigation block (if present).
+ const selectedNavId =
+ __experimentalGetActiveBlockIdByBlockNames( 'core/navigation' );
+ // Get all Navigation blocks currently within the editor canvas.
+ const navBlockIds =
+ __experimentalGetGlobalBlocksByName( 'core/navigation' );
+ const idToRef = {};
+ navBlockIds.forEach( ( id ) => {
+ idToRef[ id ] = getBlock( id )?.attributes?.ref;
+ } );
return {
- navigationPostId: navigationPost,
- navigationBlockId: selectedNavBlockId,
- isLoadingInnerBlocks: isLoading,
- hasLoadedInnerBlocks: hasLoaded,
+ selectedNavigationBlockId: selectedNavId,
+ firstNavigationBlockId: navBlockIds?.[ 0 ],
+ clientIdToRef: idToRef,
+ navigationMenus: getEntityRecords( ...navigationMenusQuery ),
+ isResolvingNavigationMenus: isResolving(
+ 'getEntityRecords',
+ navigationMenusQuery
+ ),
+ hasResolvedNavigationMenus: hasFinishedResolution(
+ 'getEntityRecords',
+ navigationMenusQuery
+ ),
};
}, [] );
+ const firstNavRefInTemplate = clientIdToRef[ firstNavigationBlockId ];
+ const firstNavigationMenuRef = navigationMenus?.[ 0 ]?.id;
+
+ // Default Navigation Menu is either:
+ // - the Navigation Menu referenced by the first Nav block within the template.
+ // - the first of the available Navigation Menus (`wp_navigation`) posts.
+ const defaultNavigationMenuId =
+ firstNavRefInTemplate || firstNavigationMenuRef;
+
+ // The Navigation Menu manually selected by the user within the Nav inspector.
+ const [ currentMenuId, setCurrentMenuId ] = useState(
+ firstNavRefInTemplate
+ );
+
+ // If a Nav block is selected within the canvas then set the
+ // Navigation Menu referenced by it's `ref` attribute to be
+ // active within the Navigation sidebar.
+ useEffect( () => {
+ if ( selectedNavigationBlockId ) {
+ setCurrentMenuId( clientIdToRef[ selectedNavigationBlockId ] );
+ }
+ }, [ selectedNavigationBlockId ] );
+
+ const [ innerBlocks, onInput, onChange ] = useEntityBlockEditor(
+ 'postType',
+ 'wp_navigation',
+ { id: currentMenuId || defaultNavigationMenuId }
+ );
+
+ const { isLoadingInnerBlocks, hasLoadedInnerBlocks } = useSelect(
+ ( select ) => {
+ const { isResolving, hasFinishedResolution } = select( coreStore );
+ return {
+ isLoadingInnerBlocks: isResolving( 'getEntityRecord', [
+ 'postType',
+ 'wp_navigation',
+ currentMenuId || defaultNavigationMenuId,
+ ] ),
+ hasLoadedInnerBlocks: hasFinishedResolution(
+ 'getEntityRecord',
+ [
+ 'postType',
+ 'wp_navigation',
+ currentMenuId || defaultNavigationMenuId,
+ ]
+ ),
+ };
+ },
+ [ currentMenuId, defaultNavigationMenuId ]
+ );
+
+ const isLoading = ! ( hasResolvedNavigationMenus && hasLoadedInnerBlocks );
+
+ const hasNavigationMenus = !! navigationMenus?.length;
+
+ // Entity block editor will return entities that are not currently published.
+ // Guard by only allowing their usage if there are published Nav Menus.
+ const publishedInnerBlocks = hasNavigationMenus ? innerBlocks : [];
+
+ const hasInnerBlocks = !! publishedInnerBlocks?.length;
+
+ useEffect( () => {
+ if ( isResolvingNavigationMenus ) {
+ speak( 'Loading Navigation sidebar menus.' );
+ }
+
+ if ( hasResolvedNavigationMenus ) {
+ speak( 'Navigation sidebar menus have loaded.' );
+ }
+ }, [ isResolvingNavigationMenus, hasResolvedNavigationMenus ] );
+
useEffect( () => {
if ( isLoadingInnerBlocks ) {
speak( 'Loading Navigation sidebar selected menu items.' );
@@ -115,39 +152,37 @@ export default function NavigationInspector( { onSelect } ) {
return (
- { hasLoadedInnerBlocks &&
- ! isLoadingInnerBlocks &&
- ! navigationBlockId &&
- ! navigationPostId && (
-
- { __( 'There are no Navigation Menus.' ) }
-
- ) }
-
- { ! hasLoadedInnerBlocks && (
+ { hasResolvedNavigationMenus && ! hasNavigationMenus && (
+
+ { __( 'There are no Navigation Menus.' ) }
+
+ ) }
+
+ { ! hasResolvedNavigationMenus && (
) }
- { isLoadingInnerBlocks && (
+ { isLoading && (
<>
>
) }
- { navigationBlockId && navigationPostId && hasLoadedInnerBlocks && (
-
+ { hasInnerBlocks && ! isLoading && (
+
+
+
+ ) }
+
+ { ! hasInnerBlocks && ! isLoading && (
+
+ { __( 'Navigation Menu is empty.' ) }
+
) }
- { ! navigationBlockId &&
- navigationPostId &&
- hasLoadedInnerBlocks && (
-
- ) }
);
}
diff --git a/packages/edit-site/src/components/navigation-inspector/navigation-menu.js b/packages/edit-site/src/components/navigation-inspector/navigation-menu.js
index 7e2255f874ddb..56ad6b9fac762 100644
--- a/packages/edit-site/src/components/navigation-inspector/navigation-menu.js
+++ b/packages/edit-site/src/components/navigation-inspector/navigation-menu.js
@@ -4,30 +4,71 @@
import {
privateApis as blockEditorPrivateApis,
store as blockEditorStore,
+ BlockList,
+ BlockTools,
} from '@wordpress/block-editor';
-import { useSelect } from '@wordpress/data';
+import { useEffect } from '@wordpress/element';
+import { useSelect, useDispatch } from '@wordpress/data';
/**
* Internal dependencies
*/
import { unlock } from '../../private-apis';
-/**
- * Experimental dependencies
- */
-const { OffCanvasEditor, LeafMoreMenu } = unlock( blockEditorPrivateApis );
+const ALLOWED_BLOCKS = {
+ 'core/navigation': [
+ 'core/navigation-link',
+ 'core/search',
+ 'core/social-links',
+ 'core/page-list',
+ 'core/spacer',
+ 'core/home-link',
+ 'core/site-title',
+ 'core/site-logo',
+ 'core/navigation-submenu',
+ ],
+ 'core/social-links': [ 'core/social-link' ],
+ 'core/navigation-submenu': [
+ 'core/navigation-link',
+ 'core/navigation-submenu',
+ ],
+ 'core/navigation-link': [
+ 'core/navigation-link',
+ 'core/navigation-submenu',
+ ],
+ 'core/page-list': [ 'core/page-list-item' ],
+};
-export default function NavigationMenu( { onSelect, navigationBlockId } ) {
- const { clientIdsTree } = useSelect(
- ( select ) => {
- const { __unstableGetClientIdsTree } = select( blockEditorStore );
- return {
- clientIdsTree: __unstableGetClientIdsTree( navigationBlockId ),
- };
- },
- [ navigationBlockId ]
- );
+export default function NavigationMenu( { onSelect } ) {
+ const { clientIdsTree, innerBlocks } = useSelect( ( select ) => {
+ const { __unstableGetClientIdsTree, getBlocks } =
+ select( blockEditorStore );
+ return {
+ clientIdsTree: __unstableGetClientIdsTree(),
+ innerBlocks: getBlocks(),
+ };
+ } );
+ const { updateBlockListSettings } = useDispatch( blockEditorStore );
+
+ const { OffCanvasEditor, LeafMoreMenu } = unlock( blockEditorPrivateApis );
+
+ //TODO: Block settings are normally updated as a side effect of rendering InnerBlocks in BlockList
+ //Think through a better way of doing this, possible with adding allowed blocks to block library metadata
+ useEffect( () => {
+ updateBlockListSettings( '', {
+ allowedBlocks: ALLOWED_BLOCKS[ 'core/navigation' ],
+ } );
+ innerBlocks.forEach( ( block ) => {
+ if ( ALLOWED_BLOCKS[ block.name ] ) {
+ updateBlockListSettings( block.clientId, {
+ allowedBlocks: ALLOWED_BLOCKS[ block.name ],
+ } );
+ }
+ } );
+ }, [ updateBlockListSettings, innerBlocks ] );
+ // The hidden block is needed because it makes block edit side effects trigger.
+ // For example a navigation page list load its items has an effect on edit to load its items.
return (
<>
+
+
+
+
+
>
);
}