@wordpress/block-library
Version:
Block library for the WordPress editor.
134 lines (118 loc) • 3.75 kB
JavaScript
/**
* WordPress dependencies
*/
import { createBlock } from '@wordpress/blocks';
import { useDispatch } from '@wordpress/data';
import { store as blockEditorStore } from '@wordpress/block-editor';
/**
* Converts an array of pages into a nested array of navigation link blocks.
*
* @param {Array} pages An array of pages.
*
* @return {Array} A nested array of navigation link blocks.
*/
function createNavigationLinks( pages = [] ) {
const linkMap = {};
const navigationLinks = [];
pages.forEach( ( { id, title, link: url, type, parent } ) => {
// See if a placeholder exists. This is created if children appear before parents in list.
const innerBlocks = linkMap[ id ]?.innerBlocks ?? [];
linkMap[ id ] = createBlock(
'core/navigation-link',
{
id,
label: title.rendered,
url,
type,
kind: 'post-type',
},
innerBlocks
);
if ( ! parent ) {
navigationLinks.push( linkMap[ id ] );
} else {
if ( ! linkMap[ parent ] ) {
// Use a placeholder if the child appears before parent in list.
linkMap[ parent ] = { innerBlocks: [] };
}
// Although these variables are not referenced, they are needed to store the innerBlocks in memory.
const parentLinkInnerBlocks = linkMap[ parent ].innerBlocks;
parentLinkInnerBlocks.push( linkMap[ id ] );
}
} );
return navigationLinks;
}
/**
* Finds a navigation link block by id, recursively.
* It might be possible to make this a more generic helper function.
*
* @param {Array} navigationLinks An array of navigation link blocks.
* @param {number} id The id of the navigation link to find.
*
* @return {Object|null} The navigation link block with the given id.
*/
function findNavigationLinkById( navigationLinks, id ) {
for ( const navigationLink of navigationLinks ) {
// Is this the link we're looking for?
if ( navigationLink.attributes.id === id ) {
return navigationLink;
}
// If not does it have innerBlocks?
if ( navigationLink.innerBlocks && navigationLink.innerBlocks.length ) {
const foundNavigationLink = findNavigationLinkById(
navigationLink.innerBlocks,
id
);
if ( foundNavigationLink ) {
return foundNavigationLink;
}
}
}
return null;
}
export function convertToNavigationLinks( pages = [], parentPageID = null ) {
let navigationLinks = createNavigationLinks( pages );
// If a parent page ID is provided, only return the children of that page.
if ( parentPageID ) {
const parentPage = findNavigationLinkById(
navigationLinks,
parentPageID
);
if ( parentPage && parentPage.innerBlocks ) {
navigationLinks = parentPage.innerBlocks;
}
}
// Transform all links with innerBlocks into Submenus. This can't be done
// sooner because page objects have no information on their children.
const transformSubmenus = ( listOfLinks ) => {
listOfLinks.forEach( ( block, index, listOfLinksArray ) => {
const { attributes, innerBlocks } = block;
if ( innerBlocks.length !== 0 ) {
transformSubmenus( innerBlocks );
const transformedBlock = createBlock(
'core/navigation-submenu',
attributes,
innerBlocks
);
listOfLinksArray[ index ] = transformedBlock;
}
} );
};
transformSubmenus( navigationLinks );
return navigationLinks;
}
export function useConvertToNavigationLinks( {
clientId,
pages,
parentClientId,
parentPageID,
} ) {
const { replaceBlock, selectBlock } = useDispatch( blockEditorStore );
return () => {
const navigationLinks = convertToNavigationLinks( pages, parentPageID );
// Replace the Page List block with the Navigation Links.
replaceBlock( clientId, navigationLinks );
// Select the Navigation block to reveal the changes.
selectBlock( parentClientId );
};
}