diff --git a/packages/block-library/src/navigation/edit/index.js b/packages/block-library/src/navigation/edit/index.js index a8218263408936..5592bd52d70b3b 100644 --- a/packages/block-library/src/navigation/edit/index.js +++ b/packages/block-library/src/navigation/edit/index.js @@ -7,9 +7,9 @@ import classnames from 'classnames'; * WordPress dependencies */ import { useState, useEffect, useRef, Platform } from '@wordpress/element'; +import { addQueryArgs } from '@wordpress/url'; import { InspectorControls, - BlockControls, useBlockProps, __experimentalRecursionProvider as RecursionProvider, __experimentalUseHasRecursion as useHasRecursion, @@ -29,7 +29,6 @@ import { ToggleControl, __experimentalToggleGroupControl as ToggleGroupControl, __experimentalToggleGroupControlOption as ToggleGroupControlOption, - ToolbarGroup, Button, Spinner, } from '@wordpress/components'; @@ -136,6 +135,10 @@ function Navigation( { isError: createNavigationMenuIsError, } = useCreateNavigationMenu( clientId ); + const createUntitledEmptyNavigationMenu = () => { + createNavigationMenu( '' ); + }; + useEffect( () => { hideNavigationMenuStatusNotice(); @@ -144,8 +147,9 @@ function Navigation( { } if ( createNavigationMenuIsSuccess ) { - setRef( createNavigationMenuPost.id ); - selectBlock( clientId ); + handleUpdateMenu( createNavigationMenuPost.id, { + focusNavigationBlock: true, + } ); showNavigationMenuStatusNotice( __( `Navigation Menu successfully created.` ) @@ -158,7 +162,6 @@ function Navigation( { ); } }, [ - createNavigationMenu, createNavigationMenuStatus, createNavigationMenuError, createNavigationMenuPost, @@ -194,7 +197,6 @@ function Navigation( { isNavigationMenuResolved, isNavigationMenuMissing, navigationMenus, - navigationMenu, canUserUpdateNavigationMenu, hasResolvedCanUserUpdateNavigationMenu, canUserDeleteNavigationMenu, @@ -243,8 +245,6 @@ function Navigation( { const navRef = useRef(); - const isDraftNavigationMenu = navigationMenu?.status === 'draft'; - const { convert: convertClassicMenu, status: classicMenuConversionStatus, @@ -334,9 +334,15 @@ function Navigation( { ] = useState(); const [ detectedOverlayColor, setDetectedOverlayColor ] = useState(); - const handleUpdateMenu = ( menuId ) => { + const handleUpdateMenu = ( + menuId, + options = { focusNavigationBlock: false } + ) => { + const { focusNavigationBlock } = options; setRef( menuId ); - selectBlock( clientId ); + if ( focusNavigationBlock ) { + selectBlock( clientId ); + } }; useEffect( () => { @@ -429,27 +435,8 @@ function Navigation( { ref, ] ); - const navigationSelectorRef = useRef(); - const [ shouldFocusNavigationSelector, setShouldFocusNavigationSelector ] = - useState( false ); - - // Focus support after menu selection. - useEffect( () => { - if ( - isDraftNavigationMenu || - ! isEntityAvailable || - ! shouldFocusNavigationSelector - ) { - return; - } - navigationSelectorRef?.current?.focus(); - setShouldFocusNavigationSelector( false ); - }, [ - isDraftNavigationMenu, - isEntityAvailable, - shouldFocusNavigationSelector, - ] ); - + const hasManagePermissions = + canUserCreateNavigationMenu || canUserUpdateNavigationMenu; const isResponsive = 'never' !== overlayMenu; const overlayMenuPreviewClasses = classnames( @@ -603,15 +590,13 @@ function Navigation( { if ( hasUnsavedBlocks ) { return ( - - + + { handleUpdateMenu( menuId ); - setShouldFocusNavigationSelector( true ); } } onSelectClassicMenu={ async ( classicMenu ) => { const navMenu = await convertClassicMenu( @@ -619,17 +604,32 @@ function Navigation( { classicMenu.name ); if ( navMenu ) { - handleUpdateMenu( navMenu.id ); - setShouldFocusNavigationSelector( true ); + handleUpdateMenu( navMenu.id, { + focusNavigationBlock: true, + } ); } } } - onCreateNew={ () => createNavigationMenu( '', [] ) } + onCreateNew={ createUntitledEmptyNavigationMenu } + createNavigationMenuIsSuccess={ + createNavigationMenuIsSuccess + } /* translators: %s: The name of a menu. */ actionLabel={ __( "Switch to '%s'" ) } - showManageActions /> - - + + + { stylingInspectorControls } - - + + { handleUpdateMenu( menuId ); - setShouldFocusNavigationSelector( true ); } } onSelectClassicMenu={ async ( classicMenu ) => { const navMenu = await convertClassicMenu( @@ -687,23 +685,38 @@ function Navigation( { classicMenu.name ); if ( navMenu ) { - handleUpdateMenu( navMenu.id ); - setShouldFocusNavigationSelector( true ); + handleUpdateMenu( navMenu.id, { + focusNavigationBlock: true, + } ); } } } - onCreateNew={ () => createNavigationMenu( '', [] ) } + onCreateNew={ createUntitledEmptyNavigationMenu } + createNavigationMenuIsSuccess={ + createNavigationMenuIsSuccess + } /* translators: %s: The name of a menu. */ actionLabel={ __( "Switch to '%s'" ) } - showManageActions /> - - + + + { __( 'Navigation menu has been deleted or is unavailable. ' ) } + + { stylingInspectorControls } { isEntityAvailable && ( diff --git a/packages/block-library/src/navigation/edit/navigation-menu-selector.js b/packages/block-library/src/navigation/edit/navigation-menu-selector.js index b83280ec129ec3..04edb53236dae8 100644 --- a/packages/block-library/src/navigation/edit/navigation-menu-selector.js +++ b/packages/block-library/src/navigation/edit/navigation-menu-selector.js @@ -5,12 +5,15 @@ import { MenuGroup, MenuItem, MenuItemsChoice, - ToolbarDropdownMenu, + DropdownMenu, + Button, + VisuallyHidden, } from '@wordpress/components'; +import { useEntityProp } from '@wordpress/core-data'; +import { Icon, chevronUp, chevronDown } from '@wordpress/icons'; import { __, sprintf } from '@wordpress/i18n'; import { decodeEntities } from '@wordpress/html-entities'; -import { addQueryArgs } from '@wordpress/url'; -import { forwardRef, useMemo } from '@wordpress/element'; +import { useEffect, useMemo, useState } from '@wordpress/element'; /** * Internal dependencies @@ -18,36 +21,55 @@ import { forwardRef, useMemo } from '@wordpress/element'; import useNavigationMenu from '../use-navigation-menu'; import useNavigationEntities from '../use-navigation-entities'; -function NavigationMenuSelector( - { - currentMenuId, - onSelectNavigationMenu, - onSelectClassicMenu, - onCreateNew, - showManageActions = false, - actionLabel, - toggleProps = {}, - }, - forwardedRef -) { +function NavigationMenuSelector( { + currentMenuId, + onSelectNavigationMenu, + onSelectClassicMenu, + onCreateNew, + actionLabel, + createNavigationMenuIsSuccess, + createNavigationMenuIsError, + toggleProps = {}, +} ) { /* translators: %s: The name of a menu. */ const createActionLabel = __( "Create from '%s'" ); + const [ selectorLabel, setSelectorLabel ] = useState( '' ); + const [ isPressed, setIsPressed ] = useState( false ); + const [ enableOptions, setEnableOptions ] = useState( false ); + const [ isCreatingMenu, setIsCreatingMenu ] = useState( false ); + actionLabel = actionLabel || createActionLabel; const { menus: classicMenus } = useNavigationEntities(); const { navigationMenus, + hasResolvedNavigationMenus, + isNavigationMenuResolved, canUserCreateNavigationMenu, - canUserUpdateNavigationMenu, canSwitchNavigationMenu, } = useNavigationMenu(); + const [ currentTitle ] = useEntityProp( + 'postType', + 'wp_navigation', + 'title' + ); + + const shouldEnableMenuSelector = + ( canSwitchNavigationMenu || canUserCreateNavigationMenu ) && + hasResolvedNavigationMenus && + ! isCreatingMenu; + const menuChoices = useMemo( () => { return ( navigationMenus?.map( ( { id, title } ) => { const label = decodeEntities( title.rendered ); + if ( id === currentMenuId && ! isCreatingMenu ) { + setSelectorLabel( currentTitle ); + setEnableOptions( shouldEnableMenuSelector ); + } return { value: id, label, @@ -55,32 +77,90 @@ function NavigationMenuSelector( }; } ) || [] ); - }, [ navigationMenus ] ); + }, [ + currentTitle, + currentMenuId, + navigationMenus, + createNavigationMenuIsSuccess, + isNavigationMenuResolved, + hasResolvedNavigationMenus, + ] ); const hasNavigationMenus = !! navigationMenus?.length; const hasClassicMenus = !! classicMenus?.length; const showNavigationMenus = !! canSwitchNavigationMenu; const showClassicMenus = !! canUserCreateNavigationMenu; - const hasManagePermissions = - canUserCreateNavigationMenu || canUserUpdateNavigationMenu; - - // Show the selector if: - // - has switch or create permissions and there are block or classic menus. - // - user has create or update permissions and component should show the menu actions. - const showSelectMenus = - ( ( canSwitchNavigationMenu || canUserCreateNavigationMenu ) && - ( hasNavigationMenus || hasClassicMenus ) ) || - ( hasManagePermissions && showManageActions ); - - if ( ! showSelectMenus ) { - return null; + + const noMenuSelected = hasNavigationMenus && ! currentMenuId; + const noBlockMenus = ! hasNavigationMenus && hasResolvedNavigationMenus; + const menuUnavailable = + hasResolvedNavigationMenus && currentMenuId === null; + + useEffect( () => { + if ( ! hasResolvedNavigationMenus ) { + setSelectorLabel( __( 'Loading …' ) ); + } else if ( noMenuSelected || noBlockMenus || menuUnavailable ) { + setSelectorLabel( __( 'Select menu' ) ); + setEnableOptions( shouldEnableMenuSelector ); + } + + if ( + isCreatingMenu && + ( createNavigationMenuIsSuccess || createNavigationMenuIsError ) + ) { + setIsCreatingMenu( false ); + } + }, [ + currentMenuId, + hasNavigationMenus, + hasResolvedNavigationMenus, + createNavigationMenuIsSuccess, + isNavigationMenuResolved, + ] ); + + toggleProps = { + ...toggleProps, + className: 'wp-block-navigation__navigation-selector-button', + children: ( + <> + + { __( 'Select Menu' ) } + + + + ), + isBusy: ! enableOptions, + onClick: () => { + setIsPressed( ! isPressed ); + }, + }; + + if ( ! hasNavigationMenus && ! hasClassicMenus ) { + return ( + + ); } return ( - @@ -91,7 +171,6 @@ function NavigationMenuSelector( { - onClose(); onSelectNavigationMenu( menuId ); } } choices={ menuChoices } @@ -105,8 +184,12 @@ function NavigationMenuSelector( return ( { - onClose(); + setSelectorLabel( + __( 'Loading …' ) + ); + setEnableOptions( false ); onSelectClassicMenu( menu ); + onClose(); } } key={ menu.id } aria-label={ sprintf( @@ -121,26 +204,25 @@ function NavigationMenuSelector( ) } - { showManageActions && hasManagePermissions && ( + { canUserCreateNavigationMenu && ( - { canUserCreateNavigationMenu && ( - - { __( 'Create new menu' ) } - - ) } { + onClose(); + onCreateNew(); + setIsCreatingMenu( true ); + setSelectorLabel( __( 'Loading …' ) ); + setEnableOptions( false ); + } } > - { __( 'Manage menus' ) } + { __( 'Create new menu' ) } ) } ) } - + ); } -export default forwardRef( NavigationMenuSelector ); +export default NavigationMenuSelector; diff --git a/packages/block-library/src/navigation/editor.scss b/packages/block-library/src/navigation/editor.scss index 5e97102c99fd33..b6444c1a331811 100644 --- a/packages/block-library/src/navigation/editor.scss +++ b/packages/block-library/src/navigation/editor.scss @@ -615,3 +615,23 @@ body.editor-styles-wrapper // Override the default padding of ToggleGroupControlOption. padding: 0 3px; } + +/** + * Navigation selector styles + */ +.wp-block-navigation__navigation-selector { + margin-bottom: $grid-unit-20; + width: 100%; +} + +.wp-block-navigation__navigation-selector-button { + border: 1px solid; + justify-content: space-between; + width: 100%; +} + +.wp-block-navigation__navigation-selector-button--createnew { + border: 1px solid; + margin-bottom: $grid-unit-20; + width: 100%; +}