diff --git a/packages/block-library/src/page-list/constants.js b/packages/block-library/src/page-list/constants.js deleted file mode 100644 index 9322fa345321c4..00000000000000 --- a/packages/block-library/src/page-list/constants.js +++ /dev/null @@ -1,8 +0,0 @@ -/** - * WordPress dependencies - */ -import { __ } from '@wordpress/i18n'; - -export const convertDescription = __( - 'This menu is automatically kept in sync with pages on your site. You can manage the menu yourself by clicking customize below.' -); diff --git a/packages/block-library/src/page-list/convert-to-links-modal.js b/packages/block-library/src/page-list/convert-to-links-modal.js deleted file mode 100644 index dbc76fc77cdb1e..00000000000000 --- a/packages/block-library/src/page-list/convert-to-links-modal.js +++ /dev/null @@ -1,70 +0,0 @@ -/** - * WordPress dependencies - */ -import { Button, Modal } from '@wordpress/components'; -import { __ } from '@wordpress/i18n'; -import { useDispatch } from '@wordpress/data'; -import { useEntityRecords } from '@wordpress/core-data'; -import { store as blockEditorStore } from '@wordpress/block-editor'; -/** - * Internal dependencies - */ -import { convertToNavigationLinks } from './convert-to-navigation-links'; - -/** - * Internal dependencies - */ -import { convertDescription } from './constants'; - -const PAGE_FIELDS = [ 'id', 'title', 'link', 'type', 'parent' ]; -const MAX_PAGE_COUNT = 100; - -export default function ConvertToLinksModal( { onClose, clientId } ) { - const { records: pages, hasResolved: pagesFinished } = useEntityRecords( - 'postType', - 'page', - { - per_page: MAX_PAGE_COUNT, - _fields: PAGE_FIELDS, - // TODO: When https://core.trac.wordpress.org/ticket/39037 REST API support for multiple orderby - // values is resolved, update 'orderby' to [ 'menu_order', 'post_title' ] to provide a consistent - // sort. - orderby: 'menu_order', - order: 'asc', - } - ); - - const { replaceBlock } = useDispatch( blockEditorStore ); - - return ( - -

- { convertDescription } -

-
- - -
-
- ); -} diff --git a/packages/block-library/src/page-list/edit.js b/packages/block-library/src/page-list/edit.js index 219822ed87944f..8a18b79ce58507 100644 --- a/packages/block-library/src/page-list/edit.js +++ b/packages/block-library/src/page-list/edit.js @@ -23,24 +23,128 @@ import { Notice, ComboboxControl, Button, + Modal, } from '@wordpress/components'; import { __, sprintf } from '@wordpress/i18n'; import { useMemo, useState, useEffect } from '@wordpress/element'; import { useEntityRecords } from '@wordpress/core-data'; -import { useSelect, useDispatch } from '@wordpress/data'; +import { useSelect } from '@wordpress/data'; /** * Internal dependencies */ -import ConvertToLinksModal from './convert-to-links-modal'; -import { convertToNavigationLinks } from './convert-to-navigation-links'; -import { convertDescription } from './constants'; +import { useConvertToNavigationLinks } from './use-convert-to-navigation-links'; // We only show the edit option when page count is <= MAX_PAGE_COUNT // Performance of Navigation Links is not good past this value. const MAX_PAGE_COUNT = 100; const NOOP = () => {}; +const convertDescription = __( + 'This menu is automatically kept in sync with pages on your site. You can manage the menu yourself by clicking customize below.' +); + +function BlockContent( { + blockProps, + innerBlocksProps, + hasResolvedPages, + blockList, + pages, + parentPageID, +} ) { + if ( ! hasResolvedPages ) { + return ( +
+ +
+ ); + } + + if ( pages === null ) { + return ( +
+ + { __( 'Page List: Cannot retrieve Pages.' ) } + +
+ ); + } + + if ( pages.length === 0 ) { + return ( +
+ + { __( 'Page List: Cannot retrieve Pages.' ) } + +
+ ); + } + + if ( blockList.length === 0 ) { + const parentPageDetails = pages.find( + ( page ) => page.id === parentPageID + ); + return ( +
+ + { sprintf( + // translators: %s: Page title. + __( '"%s" page has no children.' ), + parentPageDetails.title.rendered + ) } + +
+ ); + } + + if ( pages.length > 0 ) { + return ; + } +} + +function ConvertToLinksModal( { onClick, disabled } ) { + const [ isOpen, setOpen ] = useState( false ); + const openModal = () => setOpen( true ); + const closeModal = () => setOpen( false ); + + return ( + <> + + + { __( 'Edit' ) } + + + { isOpen && ( + +

+ { convertDescription } +

+
+ + +
+
+ ) } + + ); +} + export default function PageListEdit( { context, clientId, @@ -48,16 +152,56 @@ export default function PageListEdit( { setAttributes, } ) { const { parentPageID } = attributes; - const [ pages ] = useGetPages(); - const { pagesByParentId, totalPages, hasResolvedPages } = usePageData(); - const isNavigationChild = 'showSubmenuIcon' in context; + const { records: pages, hasResolved: hasResolvedPages } = useEntityRecords( + 'postType', + 'page', + { + per_page: MAX_PAGE_COUNT, + _fields: [ 'id', 'link', 'menu_order', 'parent', 'title', 'type' ], + // TODO: When https://core.trac.wordpress.org/ticket/39037 REST API support for multiple orderby + // values is resolved, update 'orderby' to [ 'menu_order', 'post_title' ] to provide a consistent + // sort. + orderby: 'menu_order', + order: 'asc', + } + ); + const allowConvertToLinks = - isNavigationChild && totalPages <= MAX_PAGE_COUNT; + 'showSubmenuIcon' in context && + pages?.length > 0 && + pages?.length <= MAX_PAGE_COUNT; - const [ isOpen, setOpen ] = useState( false ); - const openModal = () => setOpen( true ); - const closeModal = () => setOpen( false ); + const convertToNavigationLinks = useConvertToNavigationLinks( { + clientId, + pages, + } ); + + const pagesByParentId = useMemo( () => { + if ( pages === null ) { + return new Map(); + } + + // TODO: Once the REST API supports passing multiple values to + // 'orderby', this can be removed. + // https://core.trac.wordpress.org/ticket/39037 + const sortedPages = pages.sort( ( a, b ) => { + if ( a.menu_order === b.menu_order ) { + return a.title.rendered.localeCompare( b.title.rendered ); + } + return a.menu_order - b.menu_order; + } ); + + return sortedPages.reduce( ( accumulator, page ) => { + const { parent } = page; + if ( accumulator.has( parent ) ) { + accumulator.get( parent ).push( page ); + } else { + accumulator.set( parent, [ page ] ); + } + return accumulator; + }, new Map() ); + }, [ pages ] ); const blockProps = useBlockProps( { className: classnames( 'wp-block-page-list', { @@ -137,77 +281,16 @@ export default function PageListEdit( { value: blockList, } ); - const getBlockContent = () => { - if ( ! hasResolvedPages ) { - return ( -
- -
- ); - } - - if ( totalPages === null ) { - return ( -
- - { __( 'Page List: Cannot retrieve Pages.' ) } - -
- ); - } - - if ( totalPages === 0 ) { - return ( -
- - { __( 'Page List: Cannot retrieve Pages.' ) } - -
- ); - } - - if ( blockList.length === 0 ) { - const parentPageDetails = - pages && pages.find( ( page ) => page.id === parentPageID ); - return ( -
- - { sprintf( - // translators: %s: Page title. - __( '"%s" page has no children.' ), - parentPageDetails.title.rendered - ) } - -
- ); - } - - if ( totalPages > 0 ) { - return ; - } - }; - - const { replaceBlock, selectBlock } = useDispatch( blockEditorStore ); - - const { parentNavBlockClientId, isNested } = useSelect( + const { isNested } = useSelect( ( select ) => { - const { getSelectedBlockClientId, getBlockParentsByBlockName } = - select( blockEditorStore ); - - const _selectedBlockClientId = getSelectedBlockClientId(); - + const { getBlockParentsByBlockName } = select( blockEditorStore ); + const blockParents = getBlockParentsByBlockName( + clientId, + 'core/navigation-submenu', + true + ); return { - parentNavBlockClientId: getBlockParentsByBlockName( - _selectedBlockClientId, - 'core/navigation', - true - )[ 0 ], - isNested: - getBlockParentsByBlockName( - clientId, - 'core/navigation-submenu', - true - ).length > 0, + isNested: blockParents.length > 0, }; }, [ clientId ] @@ -220,22 +303,13 @@ export default function PageListEdit( { return ( <> - { isNavigationChild && pages?.length > 0 && ( + { allowConvertToLinks && (

{ convertDescription }

@@ -258,69 +332,20 @@ export default function PageListEdit( {
) }
- { allowConvertToLinks && totalPages > 0 && ( - - - { __( 'Edit' ) } - - - ) } - { allowConvertToLinks && isOpen && ( + { allowConvertToLinks && ( ) } - - { getBlockContent() } + ); } - -function useGetPages() { - const { records: pages, hasResolved: hasResolvedPages } = useEntityRecords( - 'postType', - 'page', - { - orderby: 'menu_order', - order: 'asc', - _fields: [ 'id', 'link', 'parent', 'title', 'menu_order' ], - per_page: -1, - context: 'view', - } - ); - - return [ pages, hasResolvedPages ]; -} - -function usePageData( pageId = 0 ) { - const [ pages, hasResolvedPages ] = useGetPages(); - - return useMemo( () => { - // TODO: Once the REST API supports passing multiple values to - // 'orderby', this can be removed. - // https://core.trac.wordpress.org/ticket/39037 - - const sortedPages = [ ...( pages ?? [] ) ].sort( ( a, b ) => { - if ( a.menu_order === b.menu_order ) { - return a.title.rendered.localeCompare( b.title.rendered ); - } - return a.menu_order - b.menu_order; - } ); - const pagesByParentId = sortedPages.reduce( ( accumulator, page ) => { - const { parent } = page; - if ( accumulator.has( parent ) ) { - accumulator.get( parent ).push( page ); - } else { - accumulator.set( parent, [ page ] ); - } - return accumulator; - }, new Map() ); - - return { - pagesByParentId, - hasResolvedPages, - totalPages: pages?.length ?? null, - }; - }, [ pageId, pages, hasResolvedPages ] ); -} diff --git a/packages/block-library/src/page-list/test/convert-to-links-modal.js b/packages/block-library/src/page-list/test/convert-to-links-modal.js index 4c83b64f9bb9f9..d7907cbf2e4d4a 100644 --- a/packages/block-library/src/page-list/test/convert-to-links-modal.js +++ b/packages/block-library/src/page-list/test/convert-to-links-modal.js @@ -2,7 +2,7 @@ * Internal dependencies */ -import { convertToNavigationLinks } from '../convert-to-navigation-links'; +import { convertToNavigationLinks } from '../use-convert-to-navigation-links'; // Mock createBlock to avoid creating the blocks in test environment // as convertToNavigationLinks calls this method internally. diff --git a/packages/block-library/src/page-list/convert-to-navigation-links.js b/packages/block-library/src/page-list/use-convert-to-navigation-links.js similarity index 59% rename from packages/block-library/src/page-list/convert-to-navigation-links.js rename to packages/block-library/src/page-list/use-convert-to-navigation-links.js index 64b1dbf1c7d4d6..929fb706e01bab 100644 --- a/packages/block-library/src/page-list/convert-to-navigation-links.js +++ b/packages/block-library/src/page-list/use-convert-to-navigation-links.js @@ -2,12 +2,10 @@ * WordPress dependencies */ import { createBlock } from '@wordpress/blocks'; +import { useSelect, useDispatch } from '@wordpress/data'; +import { store as blockEditorStore } from '@wordpress/block-editor'; -export const convertToNavigationLinks = ( pages ) => { - if ( ! pages ) { - return; - } - +export function convertToNavigationLinks( pages = [] ) { const linkMap = {}; const navigationLinks = []; pages.forEach( ( { id, title, link: url, type, parent } ) => { @@ -57,4 +55,36 @@ export const convertToNavigationLinks = ( pages ) => { transformSubmenus( navigationLinks ); return navigationLinks; -}; +} + +export function useConvertToNavigationLinks( { clientId, pages } ) { + const { replaceBlock, selectBlock } = useDispatch( blockEditorStore ); + + const { parentNavBlockClientId } = useSelect( + ( select ) => { + const { getSelectedBlockClientId, getBlockParentsByBlockName } = + select( blockEditorStore ); + + const _selectedBlockClientId = getSelectedBlockClientId(); + + return { + parentNavBlockClientId: getBlockParentsByBlockName( + _selectedBlockClientId, + 'core/navigation', + true + )[ 0 ], + }; + }, + [ clientId ] + ); + + return () => { + const navigationLinks = convertToNavigationLinks( pages ); + + // Replace the Page List block with the Navigation Links. + replaceBlock( clientId, navigationLinks ); + + // Select the Navigation block to reveal the changes. + selectBlock( parentNavBlockClientId ); + }; +}