diff --git a/packages/edit-navigation/src/components/header/manage-locations.js b/packages/edit-navigation/src/components/header/manage-locations.js index 1f75db5c6df7a7..e69de29bb2d1d6 100644 --- a/packages/edit-navigation/src/components/header/manage-locations.js +++ b/packages/edit-navigation/src/components/header/manage-locations.js @@ -1,45 +0,0 @@ -/** - * WordPress dependencies - */ -import { Spinner, SelectControl } from '@wordpress/components'; -import { __ } from '@wordpress/i18n'; - -/** - * Internal dependencies - */ -import useMenuLocations from '../../hooks/use-menu-locations'; - -export default function ManageLocations( { menus } ) { - const [ menuLocations, assignMenuToLocation ] = useMenuLocations(); - - if ( ! menus || ! menuLocations ) { - return ; - } - - if ( ! menus.length ) { - return

{ __( 'There are no available menus.' ) }

; - } - - if ( ! menuLocations.length ) { - return

{ __( 'There are no available menu locations.' ) }

; - } - - return menuLocations.map( ( menuLocation ) => ( - ( { - value: menu.id, - label: menu.name, - } ) ), - ] } - onChange={ ( menuId ) => { - assignMenuToLocation( menuLocation.name, Number( menuId ) ); - } } - /> - ) ); -} diff --git a/packages/edit-navigation/src/components/header/save-button.js b/packages/edit-navigation/src/components/header/save-button.js index 4d526ebbe40ed3..604a7812cc92d1 100644 --- a/packages/edit-navigation/src/components/header/save-button.js +++ b/packages/edit-navigation/src/components/header/save-button.js @@ -9,8 +9,12 @@ import { __ } from '@wordpress/i18n'; * Internal dependencies */ import { store as editNavigationStore } from '../../store'; +import { useMenuEntity, MenuIdContext } from '../../hooks'; +import { useContext } from '@wordpress/element'; export default function SaveButton( { navigationPost } ) { + const menuId = useContext( MenuIdContext ); + const { saveMenuName } = useMenuEntity( menuId ); const { saveNavigationPost } = useDispatch( editNavigationStore ); return ( @@ -19,6 +23,7 @@ export default function SaveButton( { navigationPost } ) { isPrimary onClick={ () => { saveNavigationPost( navigationPost ); + saveMenuName(); } } disabled={ ! navigationPost } > diff --git a/packages/edit-navigation/src/components/inspector-additions/index.js b/packages/edit-navigation/src/components/inspector-additions/index.js index 50fddb1987785c..11192b9b052d91 100644 --- a/packages/edit-navigation/src/components/inspector-additions/index.js +++ b/packages/edit-navigation/src/components/inspector-additions/index.js @@ -9,11 +9,19 @@ import { InspectorControls } from '@wordpress/block-editor'; */ import AutoAddPagesPanel from './auto-add-pages-panel'; import DeleteMenuPanel from './delete-menu-panel'; +import ManageLocations from './manage-locations'; import { NameEditor } from '../name-editor'; import { PanelBody } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; -export default function InspectorAdditions( { menuId, onDeleteMenu } ) { +export default function InspectorAdditions( { + menuId, + onDeleteMenu, + onSelectMenu, + isManageLocationsModalOpen, + closeManageLocationsModal, + openManageLocationsModal, +} ) { const selectedBlock = useSelect( ( select ) => select( 'core/block-editor' ).getSelectedBlock(), [] @@ -25,7 +33,15 @@ export default function InspectorAdditions( { menuId, onDeleteMenu } ) { return ( - + + + + diff --git a/packages/edit-navigation/src/components/inspector-additions/manage-locations.js b/packages/edit-navigation/src/components/inspector-additions/manage-locations.js new file mode 100644 index 00000000000000..a0736e848d5a77 --- /dev/null +++ b/packages/edit-navigation/src/components/inspector-additions/manage-locations.js @@ -0,0 +1,158 @@ +/** + * WordPress dependencies + */ +import { useSelect } from '@wordpress/data'; +import { useContext } from '@wordpress/element'; +import { + Spinner, + SelectControl, + CheckboxControl, + Button, + Modal, +} from '@wordpress/components'; +import { __, sprintf } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import { useMenuLocations, MenuIdContext } from '../../hooks'; + +export default function ManageLocations( { + onSelectMenu, + isModalOpen, + openModal, + closeModal, +} ) { + const menus = useSelect( ( select ) => select( 'core' ).getMenus(), [] ); + + const selectedMenuId = useContext( MenuIdContext ); + + const { + menuLocations, + assignMenuToLocation, + toggleMenuLocationAssignment, + } = useMenuLocations(); + + const themeLocationCountTextMain = sprintf( + // translators: Number of available theme locations. + __( + 'Your current theme provides %d different locations to place menu.' + ), + menuLocations.length + ); + + const themeLocationCountTextModal = sprintf( + // translators: Number of available theme locations. + __( + 'Your current theme supports %d different locations. Select which menu appears in each location.' + ), + menuLocations.length + ); + + const menusWithSelection = menuLocations.map( ( { name, menu } ) => { + const menuOnLocation = menus + .filter( ( { id } ) => ! [ 0, selectedMenuId ].includes( id ) ) + .find( ( { id } ) => id === menu ); + + return ( +
  • + + toggleMenuLocationAssignment( name, selectedMenuId ) + } + label={ name } + help={ + menuOnLocation && + sprintf( + // translators: menu name. + __( 'Currently using %s' ), + menuOnLocation.name + ) + } + /> +
  • + ); + } ); + + if ( ! menus || ! menuLocations ) { + return ; + } + + if ( ! menuLocations.length ) { + return

    { __( 'There are no available menu locations.' ) }

    ; + } + + const menuLocationCard = menuLocations.map( ( menuLocation ) => ( +
    + ( { + key: id, + value: id, + label: name, + } ) ), + ] } + onChange={ ( menuId ) => { + assignMenuToLocation( menuLocation.name, Number( menuId ) ); + } } + /> + +
    + ) ); + + return ( + <> +
    + { themeLocationCountTextMain } +
    +
      + { menusWithSelection } +
    + + { isModalOpen && ( + +
    + { themeLocationCountTextModal } +
    + { menuLocationCard } +
    + ) } + + ); +} diff --git a/packages/edit-navigation/src/components/inspector-additions/style.scss b/packages/edit-navigation/src/components/inspector-additions/style.scss index 359310cfb6e0b9..f0074ddb591d52 100644 --- a/packages/edit-navigation/src/components/inspector-additions/style.scss +++ b/packages/edit-navigation/src/components/inspector-additions/style.scss @@ -1,3 +1,59 @@ .edit-navigation-inspector-additions__delete-menu-panel { text-align: center; } + +.block-editor-block-inspector__no-blocks, +.block-editor-block-inspector { + width: $sidebar-width; + background: $white; + border-left: 1px solid $gray-300; + position: fixed; + top: $grid-unit-40; + right: 0; + bottom: 0; + z-index: 1; + overflow: auto; +} + +.edit-navigation-manage-locations__modal { + max-width: 360px; +} + +.edit-navigation-manage-locations__delete-menu-panel { + text-align: center; +} + +.edit-navigation-manage-locations__menu-entry .components-base-control { + width: 100%; +} + +.edit-navigation-manage-locations__edit-button { + flex: 1; +} + +.edit-navigation-manage-locations__menu-entry { + display: flex; + margin-bottom: $grid-unit-15; + margin-top: $grid-unit-15; + .components-custom-select-control, + .components-custom-select-control__button { + width: 100%; + } + button { + height: 100%; + } +} + +.edit-navigation-manage-locations__select-menu { + padding-right: $grid-unit-10; +} + +.edit-navigation-manage-locations__open-menu-locations-modal-button { + width: 100%; + justify-content: center; +} + +.edit-navigation-manage-locations__theme-location-text-main, +.edit-navigation-manage-locations__theme-location-text-modal { + margin-bottom: $grid-unit-30; +} diff --git a/packages/edit-navigation/src/components/layout/index.js b/packages/edit-navigation/src/components/layout/index.js index dd36aec418ef40..f815dac4c22b13 100644 --- a/packages/edit-navigation/src/components/layout/index.js +++ b/packages/edit-navigation/src/components/layout/index.js @@ -74,6 +74,9 @@ export default function Layout( { blockEditorSettings } ) { navigationPost, selectMenu, deleteMenu, + openManageLocationsModal, + closeManageLocationsModal, + isManageLocationsModalOpen, } = useNavigationEditor(); const [ blocks, onInput, onChange ] = useNavigationBlockEditor( @@ -167,6 +170,18 @@ export default function Layout( { blockEditorSettings } ) { blocks={ blocks } /> + Object.values( menuLocationsByName ) + .filter( ( { menu } ) => menu === id ) + .map( ( { name } ) => name ); + +const withLocations = ( menuLocationsByName ) => ( id ) => ( { + id, + locations: locationsForMenuId( menuLocationsByName, id ), +} ); + export default function useMenuLocations() { const [ menuLocationsByName, setMenuLocationsByName ] = useState( null ); @@ -14,7 +29,7 @@ export default function useMenuLocations() { const fetchMenuLocationsByName = async () => { const newMenuLocationsByName = await apiFetch( { method: 'GET', - path: '/__experimental/menu-locations', + path: '/__experimental/menu-locations/', } ); if ( isMounted ) { @@ -23,7 +38,6 @@ export default function useMenuLocations() { }; fetchMenuLocationsByName(); - return () => ( isMounted = false ); }, [] ); @@ -33,50 +47,36 @@ export default function useMenuLocations() { async ( locationName, newMenuId ) => { const oldMenuId = menuLocationsByName[ locationName ].menu; - const newMenuLocationsByName = { - ...menuLocationsByName, - [ locationName ]: { - ...menuLocationsByName[ locationName ], - menu: newMenuId, - }, - }; + const newMenuLocationsByName = merge( menuLocationsByName, { + [ locationName ]: { menu: newMenuId }, + } ); setMenuLocationsByName( newMenuLocationsByName ); - const promises = []; - - if ( oldMenuId ) { - promises.push( - saveMenu( { - id: oldMenuId, - locations: Object.values( newMenuLocationsByName ) - .filter( ( { menu } ) => menu === oldMenuId ) - .map( ( { name } ) => name ), - } ) - ); - } - - if ( newMenuId ) { - promises.push( - saveMenu( { - id: newMenuId, - locations: Object.values( newMenuLocationsByName ) - .filter( ( { menu } ) => menu === newMenuId ) - .map( ( { name } ) => name ), - } ) - ); - } - - await Promise.all( promises ); + [ oldMenuId, newMenuId ] + .filter( Boolean ) + .map( withLocations( newMenuLocationsByName ) ) + .forEach( saveMenu ); }, [ menuLocationsByName ] ); + const toggleMenuLocationAssignment = ( locationName, newMenuId ) => { + const idToSet = + menuLocationsByName[ locationName ].menu === newMenuId + ? 0 + : newMenuId; + assignMenuToLocation( locationName, idToSet ); + }; + const menuLocations = useMemo( - () => - menuLocationsByName ? Object.values( menuLocationsByName ) : null, + () => Object.values( menuLocationsByName || {} ), [ menuLocationsByName ] ); - return [ menuLocations, assignMenuToLocation ]; + return { + menuLocations, + assignMenuToLocation, + toggleMenuLocationAssignment, + }; } diff --git a/packages/edit-navigation/src/hooks/use-navigation-editor.js b/packages/edit-navigation/src/hooks/use-navigation-editor.js index 3af9a2b1003860..ab6aeb0638c1eb 100644 --- a/packages/edit-navigation/src/hooks/use-navigation-editor.js +++ b/packages/edit-navigation/src/hooks/use-navigation-editor.js @@ -19,6 +19,14 @@ const getMenusData = ( select ) => { }; }; export default function useNavigationEditor() { + const [ + isManageLocationsModalOpen, + setIsManageLocationsModalOpen, + ] = useState( false ); + const [ openManageLocationsModal, closeManageLocationsModal ] = [ + true, + false, + ].map( ( bool ) => () => setIsManageLocationsModalOpen( bool ) ); const { deleteMenu: _deleteMenu } = useDispatch( 'core' ); const [ selectedMenuId, setSelectedMenuId ] = useState( null ); const [ hasFinishedInitialLoad, setHasFinishedInitialLoad ] = useState( @@ -67,5 +75,8 @@ export default function useNavigationEditor() { deleteMenu, hasFinishedInitialLoad, hasLoadedMenus, + openManageLocationsModal, + closeManageLocationsModal, + isManageLocationsModalOpen, }; } diff --git a/packages/edit-navigation/src/utils/constants.js b/packages/edit-navigation/src/utils/constants.js new file mode 100644 index 00000000000000..9074d2f5e0a59e --- /dev/null +++ b/packages/edit-navigation/src/utils/constants.js @@ -0,0 +1,27 @@ +/** + * "Kind" of the menu post. + * + * @type {string} + */ +export const MENU_KIND = 'root'; + +/** + * "post type" of the menu post. + * + * @type {string} + */ +export const MENU_POST_TYPE = 'menu'; + +/** + * "Kind" of the navigation post. + * + * @type {string} + */ +export const NAVIGATION_POST_KIND = 'root'; + +/** + * "post type" of the navigation post. + * + * @type {string} + */ +export const NAVIGATION_POST_POST_TYPE = 'postType';