diff --git a/packages/block-editor/src/components/list-view/block.js b/packages/block-editor/src/components/list-view/block.js index 4b9ca17715179c..1afde32cf1ec61 100644 --- a/packages/block-editor/src/components/list-view/block.js +++ b/packages/block-editor/src/components/list-view/block.js @@ -134,6 +134,7 @@ function ListViewBlock( { collapse, BlockSettingsMenu, listViewInstanceId, + expandedState, } = useListViewContext(); const hasSiblings = siblingBlockCount > 0; @@ -336,6 +337,8 @@ function ListViewBlock( { } } disableOpenOnArrowDown __experimentalSelectBlock={ updateSelection } + expand={ expand } + expandedState={ expandedState } /> ) } diff --git a/packages/block-editor/src/components/off-canvas-editor/block.js b/packages/block-editor/src/components/off-canvas-editor/block.js index aa47b9d8bebea6..d5bc4e46c6ce97 100644 --- a/packages/block-editor/src/components/off-canvas-editor/block.js +++ b/packages/block-editor/src/components/off-canvas-editor/block.js @@ -124,7 +124,7 @@ function ListViewBlock( { [ selectBlock ] ); - const { isTreeGridMounted, expand, collapse, LeafMoreMenu } = + const { isTreeGridMounted, expand, expandedState, collapse, LeafMoreMenu } = useListViewContext(); const toggleExpanded = useCallback( @@ -334,6 +334,8 @@ function ListViewBlock( { __experimentalSelectBlock={ updateSelection } + expandedState={ expandedState } + expand={ expand } /> ) } diff --git a/packages/block-editor/src/private-apis.js b/packages/block-editor/src/private-apis.js index 0b7e8b2511aa6b..a7357311eedbe8 100644 --- a/packages/block-editor/src/private-apis.js +++ b/packages/block-editor/src/private-apis.js @@ -5,7 +5,6 @@ import * as globalStyles from './components/global-styles'; import { ExperimentalBlockEditorProvider } from './components/provider'; import { lock } from './lock-unlock'; import OffCanvasEditor from './components/off-canvas-editor'; -import LeafMoreMenu from './components/off-canvas-editor/leaf-more-menu'; import ResizableBoxPopover from './components/resizable-box-popover'; import { ComposedPrivateInserter as PrivateInserter } from './components/inserter'; import { PrivateListView } from './components/list-view'; @@ -20,7 +19,6 @@ export const privateApis = {}; lock( privateApis, { ...globalStyles, ExperimentalBlockEditorProvider, - LeafMoreMenu, OffCanvasEditor, PrivateInserter, PrivateListView, diff --git a/packages/block-editor/src/components/off-canvas-editor/leaf-more-menu.js b/packages/block-library/src/navigation/edit/leaf-more-menu.js similarity index 91% rename from packages/block-editor/src/components/off-canvas-editor/leaf-more-menu.js rename to packages/block-library/src/navigation/edit/leaf-more-menu.js index 5d8aa7824728c6..f57335ce2bef60 100644 --- a/packages/block-editor/src/components/off-canvas-editor/leaf-more-menu.js +++ b/packages/block-library/src/navigation/edit/leaf-more-menu.js @@ -11,13 +11,7 @@ import { import { DropdownMenu, MenuItem, MenuGroup } from '@wordpress/components'; import { useDispatch, useSelect } from '@wordpress/data'; import { __, sprintf } from '@wordpress/i18n'; - -/** - * Internal dependencies - */ -import { store as blockEditorStore } from '../../store'; -import BlockTitle from '../block-title'; -import { useListViewContext } from './context'; +import { BlockTitle, store as blockEditorStore } from '@wordpress/block-editor'; const POPOVER_PROPS = { className: 'block-editor-block-settings-menu__popover', @@ -30,8 +24,7 @@ const BLOCKS_THAT_CAN_BE_CONVERTED_TO_SUBMENU = [ 'core/navigation-submenu', ]; -function AddSubmenuItem( { block, onClose } ) { - const { expandedState, expand } = useListViewContext(); +function AddSubmenuItem( { block, onClose, expandedState, expand } ) { const { insertBlock, replaceBlock, replaceInnerBlocks } = useDispatch( blockEditorStore ); @@ -139,7 +132,13 @@ export default function LeafMoreMenu( props ) { > { __( 'Move down' ) } - + { + const { OffCanvasEditor } = unlock( blockEditorPrivateApis ); + // Provide a hierarchy of clientIds for the given Navigation block (clientId). // This is required else the list view will display the entire block tree. const clientIdsTree = useSelect( diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menus/leaf-more-menu.js b/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menus/leaf-more-menu.js new file mode 100644 index 00000000000000..ab8c70fc852a62 --- /dev/null +++ b/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menus/leaf-more-menu.js @@ -0,0 +1,156 @@ +/** + * WordPress dependencies + */ +import { createBlock } from '@wordpress/blocks'; +import { + addSubmenu, + chevronUp, + chevronDown, + moreVertical, +} from '@wordpress/icons'; +import { DropdownMenu, MenuItem, MenuGroup } from '@wordpress/components'; +import { useDispatch, useSelect } from '@wordpress/data'; +import { __, sprintf } from '@wordpress/i18n'; +import { BlockTitle, store as blockEditorStore } from '@wordpress/block-editor'; + +const POPOVER_PROPS = { + className: 'block-editor-block-settings-menu__popover', + position: 'bottom right', + variant: 'toolbar', +}; + +const BLOCKS_THAT_CAN_BE_CONVERTED_TO_SUBMENU = [ + 'core/navigation-link', + 'core/navigation-submenu', +]; + +function AddSubmenuItem( { block, onClose, expandedState, expand } ) { + const { insertBlock, replaceBlock, replaceInnerBlocks } = + useDispatch( blockEditorStore ); + + const clientId = block.clientId; + const isDisabled = ! BLOCKS_THAT_CAN_BE_CONVERTED_TO_SUBMENU.includes( + block.name + ); + return ( + { + const updateSelectionOnInsert = false; + const newLink = createBlock( 'core/navigation-link' ); + + if ( block.name === 'core/navigation-submenu' ) { + insertBlock( + newLink, + block.innerBlocks.length, + clientId, + updateSelectionOnInsert + ); + } else { + // Convert to a submenu if the block currently isn't one. + const newSubmenu = createBlock( + 'core/navigation-submenu', + block.attributes, + block.innerBlocks + ); + + // The following must happen as two independent actions. + // Why? Because the offcanvas editor relies on the getLastInsertedBlocksClientIds + // selector to determine which block is "active". As the UX needs the newLink to be + // the "active" block it must be the last block to be inserted. + // Therefore the Submenu is first created and **then** the newLink is inserted + // thus ensuring it is the last inserted block. + replaceBlock( clientId, newSubmenu ); + + replaceInnerBlocks( + newSubmenu.clientId, + [ newLink ], + updateSelectionOnInsert + ); + } + if ( ! expandedState[ block.clientId ] ) { + expand( block.clientId ); + } + onClose(); + } } + > + { __( 'Add submenu link' ) } + + ); +} + +export default function LeafMoreMenu( props ) { + const { block } = props; + const { clientId } = block; + const { moveBlocksDown, moveBlocksUp, removeBlocks } = + useDispatch( blockEditorStore ); + + const removeLabel = sprintf( + /* translators: %s: block name */ + __( 'Remove %s' ), + BlockTitle( { clientId, maximumLength: 25 } ) + ); + + const rootClientId = useSelect( + ( select ) => { + const { getBlockRootClientId } = select( blockEditorStore ); + + return getBlockRootClientId( clientId ); + }, + [ clientId ] + ); + + return ( + + { ( { onClose } ) => ( + <> + + { + moveBlocksUp( [ clientId ], rootClientId ); + onClose(); + } } + > + { __( 'Move up' ) } + + { + moveBlocksDown( [ clientId ], rootClientId ); + onClose(); + } } + > + { __( 'Move down' ) } + + + + + { + removeBlocks( [ clientId ], false ); + onClose(); + } } + > + { removeLabel } + + + + ) } + + ); +} diff --git a/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menus/navigation-menu-content.js b/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menus/navigation-menu-content.js index 2208af8d20772f..48bad1c80833da 100644 --- a/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menus/navigation-menu-content.js +++ b/packages/edit-site/src/components/sidebar-navigation-screen-navigation-menus/navigation-menu-content.js @@ -19,8 +19,7 @@ import { store as coreStore } from '@wordpress/core-data'; * Internal dependencies */ import { unlock } from '../../private-apis'; - -const { PrivateListView, LeafMoreMenu } = unlock( blockEditorPrivateApis ); +import LeafMoreMenu from './leaf-more-menu'; function CustomLinkAdditionalBlockUI( { block, onClose } ) { const { updateBlockAttributes } = useDispatch( blockEditorStore ); @@ -145,6 +144,7 @@ export default function NavigationMenuContent( { rootClientId, onSelect } ) { }; }, [ shouldKeepLoading, clientIdsTree, isLoading ] ); + const { PrivateListView } = unlock( blockEditorPrivateApis ); const offCanvasOnselect = useCallback( ( block ) => { if (