Skip to content

Commit

Permalink
Second approach using a single block editor.
Browse files Browse the repository at this point in the history
  • Loading branch information
jorgefilipecosta committed Feb 16, 2023
1 parent 5320c77 commit 4b61df1
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 200 deletions.
177 changes: 38 additions & 139 deletions packages/edit-site/src/components/navigation-inspector/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,9 @@
* WordPress dependencies
*/
import { useSelect } from '@wordpress/data';
import { useState, useEffect } from '@wordpress/element';
import { store as coreStore, useEntityBlockEditor } from '@wordpress/core-data';
import {
store as blockEditorStore,
BlockEditorProvider,
} from '@wordpress/block-editor';
import { useEffect } from '@wordpress/element';
import { store as coreStore } from '@wordpress/core-data';
import { store as blockEditorStore } from '@wordpress/block-editor';
import { speak } from '@wordpress/a11y';
import { __ } from '@wordpress/i18n';

Expand All @@ -16,129 +13,38 @@ import { __ } from '@wordpress/i18n';
*/
import NavigationMenu from './navigation-menu';

const NAVIGATION_MENUS_QUERY = [ { per_page: -1, status: 'publish' } ];

export default function NavigationInspector( { onSelect } ) {
const {
selectedNavigationBlockId,
clientIdToRef,
navigationMenus,
isResolvingNavigationMenus,
hasResolvedNavigationMenus,
firstNavigationBlockId,
} = useSelect( ( select ) => {
const {
__experimentalGetActiveBlockIdByBlockNames,
__experimentalGetGlobalBlocksByName,
getBlock,
} = select( blockEditorStore );

const { getEntityRecords, hasFinishedResolution, isResolving } =
select( coreStore );

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 {
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 { navigationBlockId, isLoadingInnerBlocks, hasLoadedInnerBlocks } =
useSelect( ( select ) => {
const {
__experimentalGetActiveBlockIdByBlockNames,
__experimentalGetGlobalBlocksByName,
getBlock,
} = select( blockEditorStore );
const { isResolving, hasFinishedResolution } = select( coreStore );

const [ innerBlocks, onInput, onChange ] = useEntityBlockEditor(
'postType',
'wp_navigation',
{ id: currentMenuId || defaultNavigationMenuId }
);
const selectedNavBlockId =
__experimentalGetActiveBlockIdByBlockNames(
'core/navigation'
) ||
__experimentalGetGlobalBlocksByName( 'core/navigation' )?.[ 0 ];
const selectedNavigationPost =
selectedNavBlockId &&
getBlock( selectedNavBlockId )?.attributes?.ref;

const { isLoadingInnerBlocks, hasLoadedInnerBlocks } = useSelect(
( select ) => {
const { isResolving, hasFinishedResolution } = select( coreStore );
return {
navigationBlockId: selectedNavBlockId,
isLoadingInnerBlocks: isResolving( 'getEntityRecord', [
'postType',
'wp_navigation',
currentMenuId || defaultNavigationMenuId,
selectedNavigationPost,
] ),
hasLoadedInnerBlocks: hasFinishedResolution(
'getEntityRecord',
[
'postType',
'wp_navigation',
currentMenuId || defaultNavigationMenuId,
]
[ 'postType', 'wp_navigation', selectedNavigationPost ]
),
};
},
[ 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 ) {
Expand All @@ -152,36 +58,29 @@ export default function NavigationInspector( { onSelect } ) {

return (
<div className="edit-site-navigation-inspector">
{ hasResolvedNavigationMenus && ! hasNavigationMenus && (
<p className="edit-site-navigation-inspector__empty-msg">
{ __( 'There are no Navigation Menus.' ) }
</p>
) }

{ ! hasResolvedNavigationMenus && (
{ hasLoadedInnerBlocks &&
! isLoadingInnerBlocks &&
! navigationBlockId && (
<p className="edit-site-navigation-inspector__empty-msg">
{ __( 'There are no Navigation Menus.' ) }
</p>
) }

{ ! hasLoadedInnerBlocks && (
<div className="edit-site-navigation-inspector__placeholder" />
) }
{ isLoading && (
{ isLoadingInnerBlocks && (
<>
<div className="edit-site-navigation-inspector__placeholder is-child" />
<div className="edit-site-navigation-inspector__placeholder is-child" />
<div className="edit-site-navigation-inspector__placeholder is-child" />
</>
) }
{ hasInnerBlocks && ! isLoading && (
<BlockEditorProvider
value={ publishedInnerBlocks }
onChange={ onChange }
onInput={ onInput }
>
<NavigationMenu onSelect={ onSelect } />
</BlockEditorProvider>
) }

{ ! hasInnerBlocks && ! isLoading && (
<p className="edit-site-navigation-inspector__empty-msg">
{ __( 'Navigation Menu is empty.' ) }
</p>
{ navigationBlockId && hasLoadedInnerBlocks && (
<NavigationMenu
onSelect={ onSelect }
navigationBlockId={ navigationBlockId }
/>
) }
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,83 +4,37 @@
import {
privateApis as blockEditorPrivateApis,
store as blockEditorStore,
BlockList,
BlockTools,
} from '@wordpress/block-editor';
import { useEffect } from '@wordpress/element';
import { useSelect, useDispatch } from '@wordpress/data';
import { useSelect } from '@wordpress/data';

/**
* Internal dependencies
*/
import { unlock } from '../../private-apis';

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 } ) {
const { clientIdsTree, innerBlocks } = useSelect( ( select ) => {
const { __unstableGetClientIdsTree, getBlocks } =
select( blockEditorStore );
return {
clientIdsTree: __unstableGetClientIdsTree(),
innerBlocks: getBlocks(),
};
} );
const { updateBlockListSettings } = useDispatch( blockEditorStore );

const { OffCanvasEditor, LeafMoreMenu } = unlock( blockEditorPrivateApis );
/**
* Experimental dependencies
*/
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 ] );
export default function NavigationMenu( { onSelect, navigationBlockId } ) {
const { clientIdsTree } = useSelect(
( select ) => {
const { __unstableGetClientIdsTree } = select( blockEditorStore );
return {
clientIdsTree: __unstableGetClientIdsTree( navigationBlockId ),
};
},
[ navigationBlockId ]
);

// 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 (
<>
<OffCanvasEditor
blocks={ clientIdsTree }
onSelect={ onSelect }
LeafMoreMenu={ LeafMoreMenu }
/>
<div style={ { display: 'none' } }>
<BlockTools>
<BlockList />
</BlockTools>
</div>
</>
);
}

0 comments on commit 4b61df1

Please sign in to comment.