diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md
index 38ad3e2e11bd1..a737007d4f729 100644
--- a/docs/reference-guides/core-blocks.md
+++ b/docs/reference-guides/core-blocks.md
@@ -461,7 +461,7 @@ A collection of blocks that allow visitors to get around your site. ([Source](ht
- **Name:** core/navigation
- **Category:** theme
- **Supports:** align (full, wide), ariaLabel, inserter, interactivity, layout (allowSizingOnChildren, default, ~~allowInheriting~~, ~~allowSwitching~~, ~~allowVerticalAlignment~~), spacing (blockGap, units), typography (fontSize, lineHeight), ~~html~~, ~~renaming~~
-- **Attributes:** __unstableLocation, backgroundColor, customBackgroundColor, customOverlayBackgroundColor, customOverlayTextColor, customTextColor, hasIcon, icon, maxNestingLevel, openSubmenusOnClick, overlayBackgroundColor, overlayMenu, overlayTextColor, ref, rgbBackgroundColor, rgbTextColor, showSubmenuIcon, templateLock, textColor
+- **Attributes:** __unstableLocation, backgroundColor, customBackgroundColor, customOverlayBackgroundColor, customOverlayTextColor, customTextColor, hasIcon, icon, maxNestingLevel, openSubmenusOnClick, overlayBackgroundColor, overlayId, overlayMenu, overlayTextColor, ref, rgbBackgroundColor, rgbTextColor, showSubmenuIcon, templateLock, textColor
## Custom Link
@@ -473,6 +473,15 @@ Add a page, link, or another item to your navigation. ([Source](https://github.c
- **Supports:** interactivity (clientNavigation), typography (fontSize, lineHeight), ~~html~~, ~~renaming~~, ~~reusable~~
- **Attributes:** description, id, isTopLevelLink, kind, label, opensInNewTab, rel, title, type, url
+## Navigation Overlay Close
+
+Add a Close button to your Navigation Overlay. ([Source](https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/navigation-overlay-close))
+
+- **Name:** core/navigation-overlay-close
+- **Category:** design
+- **Supports:** anchor, color (background, text, ~~link~~), dimensions (height, width), interactivity, spacing (margin, padding, units), ~~html~~, ~~multiple~~, ~~reusable~~
+- **Attributes:** hasIcon
+
## Submenu
Add a submenu to your navigation. ([Source](https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/navigation-submenu))
diff --git a/lib/blocks.php b/lib/blocks.php
index e1d4622a0f23d..53e86882da85a 100644
--- a/lib/blocks.php
+++ b/lib/blocks.php
@@ -81,6 +81,7 @@ function gutenberg_reregister_core_block_types() {
'navigation.php' => 'core/navigation',
'navigation-link.php' => 'core/navigation-link',
'navigation-submenu.php' => 'core/navigation-submenu',
+ 'navigation-overlay-close.php' => 'core/navigation-overlay-close',
'page-list.php' => 'core/page-list',
'page-list-item.php' => 'core/page-list-item',
'pattern.php' => 'core/pattern',
diff --git a/lib/experimental/blocks.php b/lib/experimental/blocks.php
index fc67f2c9d4377..4ccd98256b98e 100644
--- a/lib/experimental/blocks.php
+++ b/lib/experimental/blocks.php
@@ -77,3 +77,68 @@ function wp_enqueue_block_view_script( $block_name, $args ) {
add_filter( 'render_block', $callback, 10, 2 );
}
}
+
+function add_navigation_overlay_area( $areas ) {
+ $areas[] = array(
+ 'area' => 'navigation-overlay',
+ 'label' => _x( 'Navigation Overlay', 'template part area' ),
+ 'description' => __(
+ 'An area for navigation overlay content.'
+ ),
+ 'area_tag' => 'section',
+ 'icon' => 'handle',
+ );
+ return $areas;
+}
+add_filter( 'default_wp_template_part_areas', 'add_navigation_overlay_area', 10, 1 );
+
+function add_default_navigation_overlay_template_part( $block_template, $id, $template_type ) {
+
+ // if the template type is not template part, return the block template
+ if ( 'wp_template_part' !== $template_type ) {
+ return $block_template;
+ }
+
+ // If its not the "Core" Navigation Overlay, return the block template.
+ if ( $id !== 'core//navigation-overlay' ) {
+ return $block_template;
+ }
+
+ // If the block template is not empty, return the "found" block template.
+ // Failure to do this will override any "found" overlay template part from the Theme.
+ if ( ! empty( $block_template ) ) {
+ return $block_template;
+ }
+
+ // Return a default template part for the Navigation Overlay.
+ // This is essentially a "Core" fallback in case the Theme does not provide one.
+ $template = new WP_Block_Template();
+
+ // TODO: should we provide "$theme" here at all as this is a "Core" template.
+ $template->id = 'core' . '//' . 'navigation-overlay';
+ $template->theme = 'core';
+ $template->slug = 'navigation-overlay';
+ $template->source = 'custom';
+ $template->type = 'wp_template_part';
+ $template->title = 'Navigation Overlay';
+ $template->status = 'publish';
+ $template->has_theme_file = null;
+ $template->is_custom = false;
+ $template->modified = null;
+ $template->origin = null;
+ $template->author = null;
+
+ // Set the area to match the Navigation Overlay area.
+ $template->area = 'navigation-overlay';
+
+ // The content is the default Navigation Overlay template part. This will only be used
+ // if the Theme does not provide a template part for the Navigation Overlay.
+ // PHP is used here to allow for translation of the default template part.
+ ob_start();
+ include __DIR__ . '/navigation-overlay.php';
+ $template->content = ob_get_clean();
+
+ return $template;
+}
+
+add_filter( 'get_block_file_template', 'add_default_navigation_overlay_template_part', 10, 3 );
diff --git a/lib/experimental/navigation-overlay.php b/lib/experimental/navigation-overlay.php
new file mode 100644
index 0000000000000..c41a754178910
--- /dev/null
+++ b/lib/experimental/navigation-overlay.php
@@ -0,0 +1,9 @@
+
+
+
diff --git a/package-lock.json b/package-lock.json
index 9ca357997a7f5..46982666d7d4a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -54473,6 +54473,7 @@
"@wordpress/private-apis": "file:../private-apis",
"@wordpress/reusable-blocks": "file:../reusable-blocks",
"@wordpress/rich-text": "file:../rich-text",
+ "@wordpress/router": "file:../router",
"@wordpress/server-side-render": "file:../server-side-render",
"@wordpress/url": "file:../url",
"@wordpress/viewport": "file:../viewport",
@@ -69818,6 +69819,7 @@
"@wordpress/private-apis": "file:../private-apis",
"@wordpress/reusable-blocks": "file:../reusable-blocks",
"@wordpress/rich-text": "file:../rich-text",
+ "@wordpress/router": "file:../router",
"@wordpress/server-side-render": "file:../server-side-render",
"@wordpress/url": "file:../url",
"@wordpress/viewport": "file:../viewport",
diff --git a/packages/block-library/package.json b/packages/block-library/package.json
index b27704fcd52fb..ff0d4443b9cec 100644
--- a/packages/block-library/package.json
+++ b/packages/block-library/package.json
@@ -59,6 +59,7 @@
"@wordpress/private-apis": "file:../private-apis",
"@wordpress/reusable-blocks": "file:../reusable-blocks",
"@wordpress/rich-text": "file:../rich-text",
+ "@wordpress/router": "file:../router",
"@wordpress/server-side-render": "file:../server-side-render",
"@wordpress/url": "file:../url",
"@wordpress/viewport": "file:../viewport",
diff --git a/packages/block-library/src/index.js b/packages/block-library/src/index.js
index e2e0fd9e414ef..365a9f7805640 100644
--- a/packages/block-library/src/index.js
+++ b/packages/block-library/src/index.js
@@ -69,6 +69,7 @@ import * as more from './more';
import * as navigation from './navigation';
import * as navigationLink from './navigation-link';
import * as navigationSubmenu from './navigation-submenu';
+import * as navigationOverlayClose from './navigation-overlay-close';
import * as nextpage from './nextpage';
import * as pattern from './pattern';
import * as pageList from './page-list';
@@ -186,6 +187,7 @@ const getAllBlocks = () => {
navigation,
navigationLink,
navigationSubmenu,
+ navigationOverlayClose,
siteLogo,
siteTitle,
siteTagline,
diff --git a/packages/block-library/src/navigation-overlay-close/block.json b/packages/block-library/src/navigation-overlay-close/block.json
new file mode 100644
index 0000000000000..8df3fcc3aa74b
--- /dev/null
+++ b/packages/block-library/src/navigation-overlay-close/block.json
@@ -0,0 +1,49 @@
+{
+ "$schema": "https://schemas.wp.org/trunk/block.json",
+ "apiVersion": 3,
+ "name": "core/navigation-overlay-close",
+ "title": "Navigation Overlay Close",
+ "category": "design",
+ "description": "Add a Close button to your Navigation Overlay.",
+ "textdomain": "default",
+ "icon": "dismiss",
+ "attributes": {
+ "hasIcon": {
+ "type": "boolean",
+ "default": true
+ }
+ },
+ "usesContext": [
+ "textColor",
+ "customTextColor",
+ "backgroundColor",
+ "customBackgroundColor"
+ ],
+ "supports": {
+ "multiple": false,
+ "reusable": false,
+ "html": false,
+ "dimensions": {
+ "width": true,
+ "height": true
+ },
+ "color": {
+ "link": false,
+ "text": true,
+ "background": true
+ },
+ "interactivity": true,
+ "anchor": true,
+ "spacing": {
+ "margin": true,
+ "padding": true,
+ "units": [ "px", "em", "rem", "vh", "vw" ],
+ "__experimentalDefaultControls": {
+ "margin": true,
+ "padding": true
+ }
+ }
+ },
+ "editorStyle": "wp-block-navigation-overlay-close-editor",
+ "style": "wp-block-navigation-overlay-close"
+}
diff --git a/packages/block-library/src/navigation-overlay-close/edit.js b/packages/block-library/src/navigation-overlay-close/edit.js
new file mode 100644
index 0000000000000..72e09f77f1736
--- /dev/null
+++ b/packages/block-library/src/navigation-overlay-close/edit.js
@@ -0,0 +1,55 @@
+/**
+ * WordPress dependencies
+ */
+import { Button, Icon, ToggleControl, PanelBody } from '@wordpress/components';
+import { close } from '@wordpress/icons';
+import { __ } from '@wordpress/i18n';
+import { useBlockProps, InspectorControls } from '@wordpress/block-editor';
+import { privateApis as routerPrivateApis } from '@wordpress/router';
+
+/**
+ * Internal dependencies
+ */
+import { unlock } from '../lock-unlock';
+
+const { useHistory } = unlock( routerPrivateApis );
+
+export default function Edit( { attributes, isSelected, setAttributes } ) {
+ const blockProps = useBlockProps();
+ const history = useHistory();
+ const { hasIcon } = attributes;
+
+ const closeText = __( 'Close' );
+
+ const onClick = () => {
+ if ( isSelected ) {
+ // Exit navigation overlay edit mode.
+ history.back();
+ }
+ };
+
+ blockProps.onClick = onClick;
+
+ return (
+ <>
+
+
+
+ setAttributes( { hasIcon: value } )
+ }
+ checked={ hasIcon }
+ />
+
+
+
+ >
+ );
+}
diff --git a/packages/block-library/src/navigation-overlay-close/index.js b/packages/block-library/src/navigation-overlay-close/index.js
new file mode 100644
index 0000000000000..0a048e1e6409d
--- /dev/null
+++ b/packages/block-library/src/navigation-overlay-close/index.js
@@ -0,0 +1,16 @@
+/**
+ * Internal dependencies
+ */
+import initBlock from '../utils/init-block';
+import metadata from './block.json';
+import edit from './edit';
+
+const { name } = metadata;
+
+export { metadata, name };
+
+export const settings = {
+ edit,
+};
+
+export const init = () => initBlock( { name, metadata, settings } );
diff --git a/packages/block-library/src/navigation-overlay-close/index.php b/packages/block-library/src/navigation-overlay-close/index.php
new file mode 100644
index 0000000000000..8ebf367ba14f5
--- /dev/null
+++ b/packages/block-library/src/navigation-overlay-close/index.php
@@ -0,0 +1,48 @@
+';
+
+ $hasIcon = ! empty( $attributes['hasIcon'] );
+
+ $wrapper_attributes = get_block_wrapper_attributes(
+ array_filter( // Removes any empty attributes.
+ // Attributes
+ array(
+ // This directive is duplicated in the Navigation Block itself.
+ // See WP_Navigation_Block_Renderer::get_responsive_container_markup().
+ // Changes to this directive should be reflected there as well.
+ 'data-wp-on--click' => 'actions.closeMenuOnClick',
+ 'aria-label' => $hasIcon ? __( 'Close menu' ) : false,
+ )
+ )
+ );
+
+ $content = $hasIcon ? $close_icon : __( 'Close menu' );
+
+ return sprintf(
+ '',
+ $wrapper_attributes,
+ $content,
+ );
+
+}
+
+
+/**
+ * Registers the `core/navigation-overlay-close` block on server.
+ */
+function register_block_core_navigation_overlay_close() {
+ register_block_type_from_metadata(
+ __DIR__ . '/navigation-overlay-close',
+ array(
+ 'render_callback' => 'render_block_core_navigation_overlay_close',
+ )
+ );
+}
+add_action( 'init', 'register_block_core_navigation_overlay_close' );
diff --git a/packages/block-library/src/navigation-overlay-close/init.js b/packages/block-library/src/navigation-overlay-close/init.js
new file mode 100644
index 0000000000000..79f0492c2cb2f
--- /dev/null
+++ b/packages/block-library/src/navigation-overlay-close/init.js
@@ -0,0 +1,6 @@
+/**
+ * Internal dependencies
+ */
+import { init } from './';
+
+export default init();
diff --git a/packages/block-library/src/navigation-overlay-close/style.scss b/packages/block-library/src/navigation-overlay-close/style.scss
new file mode 100644
index 0000000000000..acfd3bc9f2b9f
--- /dev/null
+++ b/packages/block-library/src/navigation-overlay-close/style.scss
@@ -0,0 +1,34 @@
+
+// Size of burger and close icons.
+$navigation-icon-size: 24px;
+
+// Menu and close buttons.
+.wp-block-navigation-overlay-close {
+ height: auto; // remove default height applied to button component
+ vertical-align: middle;
+ cursor: pointer;
+ border: none;
+ margin: 0;
+ padding: 0;
+ text-transform: inherit;
+ z-index: 2; // Needs to be above the modal z index itself.
+ background-color: inherit; // remove user agent stylesheet default.
+ color: inherit; // remove user agent stylesheet default.
+
+ // When set to collapse into a text button, it should inherit the parent font.
+ // This needs specificity to override inherited properties by the button element and component.
+ &.wp-block-navigation-overlay-close {
+ font-family: inherit;
+ font-weight: inherit;
+ font-size: inherit;
+ }
+
+ svg {
+ fill: currentColor;
+ pointer-events: none;
+ display: block;
+ width: $navigation-icon-size;
+ height: $navigation-icon-size;
+ }
+}
+
diff --git a/packages/block-library/src/navigation/block.json b/packages/block-library/src/navigation/block.json
index eef6af390de78..81086dfd41763 100644
--- a/packages/block-library/src/navigation/block.json
+++ b/packages/block-library/src/navigation/block.json
@@ -22,7 +22,7 @@
"textdomain": "default",
"attributes": {
"ref": {
- "type": "number"
+ "type": [ "number", "string" ]
},
"textColor": {
"type": "string"
@@ -84,6 +84,9 @@
"templateLock": {
"type": [ "string", "boolean" ],
"enum": [ "all", "insert", "contentOnly", false ]
+ },
+ "overlayId": {
+ "type": "string"
}
},
"providesContext": {
diff --git a/packages/block-library/src/navigation/constants.js b/packages/block-library/src/navigation/constants.js
index 154c490e83839..9a3baf9fbb67c 100644
--- a/packages/block-library/src/navigation/constants.js
+++ b/packages/block-library/src/navigation/constants.js
@@ -7,6 +7,8 @@ export const PRIORITIZED_INSERTER_BLOCKS = [
'core/navigation-link',
];
+export const NAVIGATION_OVERLAY_TEMPLATE_PART_AREA = 'navigation-overlay';
+
// These parameters must be kept aligned with those in
// lib/compat/wordpress-6.3/navigation-block-preloading.php
// and
diff --git a/packages/block-library/src/navigation/edit/edit-overlay-button.js b/packages/block-library/src/navigation/edit/edit-overlay-button.js
new file mode 100644
index 0000000000000..2043e6246527c
--- /dev/null
+++ b/packages/block-library/src/navigation/edit/edit-overlay-button.js
@@ -0,0 +1,172 @@
+/**
+ * WordPress dependencies
+ */
+import { __ } from '@wordpress/i18n';
+import {
+ Button,
+ MenuGroup,
+ MenuItem,
+ MenuItemsChoice,
+ DropdownMenu,
+} from '@wordpress/components';
+import { useSelect, useDispatch } from '@wordpress/data';
+import { privateApis as routerPrivateApis } from '@wordpress/router';
+import { store as coreStore } from '@wordpress/core-data';
+import { parse, serialize } from '@wordpress/blocks';
+import { moreVertical } from '@wordpress/icons';
+/**
+ * Internal dependencies
+ */
+import { unlock } from '../../lock-unlock';
+import useGoToOverlayEditor from './use-go-to-overlay-editor';
+import useOverlay from './use-overlay';
+
+const { useHistory } = unlock( routerPrivateApis );
+
+export default function EditOverlayButton( {
+ navRef,
+ attributes,
+ setAttributes,
+} ) {
+ const currentOverlayId = attributes?.overlayId;
+
+ // Get any custom overlay attached to this block,
+ // falling back to the one provided by the Theme.
+ const overlay = useOverlay( currentOverlayId );
+
+ const { coreOverlay, allOverlays } = useSelect( ( select ) => {
+ return {
+ // Get the default template part that core provides.
+ coreOverlay: select( coreStore ).getEntityRecord(
+ 'postType',
+ 'wp_template_part',
+ `core//navigation-overlay`
+ ),
+ // Get all the overlays.
+ allOverlays: select( coreStore ).getEntityRecords(
+ 'postType',
+ 'wp_template_part',
+ {
+ area: 'navigation-overlay',
+ }
+ ),
+ };
+ }, [] );
+
+ const { saveEntityRecord } = useDispatch( coreStore );
+
+ const history = useHistory();
+
+ const goToOverlayEditor = useGoToOverlayEditor();
+
+ async function handleEditOverlay( event ) {
+ event.preventDefault();
+
+ // There may already be an overlay with the slug `navigation-overlay`.
+ // This might be a user created one, or one provided by the theme.
+ // If so, then go directly to the editor for that overlay template part.
+ if ( overlay ) {
+ goToOverlayEditor( overlay.id, navRef );
+ return;
+ }
+
+ // If there is not overlay then create one using the base template part
+ // provided by Core.
+ // TODO: catch and handle errors.
+ const overlayBlocks = buildOverlayBlocks( coreOverlay.content.raw );
+
+ // The new overlay should use the current Theme's slug.
+ const newOverlay = await createOverlay( overlayBlocks );
+
+ goToOverlayEditor( newOverlay?.id, navRef );
+ }
+
+ async function handleCreateNewOverlay() {
+ const overlayBlocks = buildOverlayBlocks( overlay.content.raw );
+
+ const newOverlay = await createOverlay( overlayBlocks );
+
+ setAttributes( {
+ overlayId: newOverlay?.id,
+ } );
+
+ goToOverlayEditor( newOverlay?.id, navRef );
+ }
+
+ function buildOverlayBlocks( content ) {
+ const parsedBlocks = parse( content );
+ return parsedBlocks;
+ }
+
+ async function createOverlay( overlayBlocks ) {
+ return await saveEntityRecord(
+ 'postType',
+ 'wp_template_part',
+ {
+ slug: `navigation-overlay`, // `theme//` prefix is appended automatically.
+ title: `Navigation Overlay`,
+ content: serialize( overlayBlocks ),
+ area: 'navigation-overlay',
+ },
+ { throwOnError: true }
+ );
+ }
+
+ // Map the overlay records to format
+ const overlayChoices = allOverlays?.map( ( overlayRecord ) => {
+ return {
+ label: overlayRecord.title.rendered, // decodeEntities required
+ value: overlayRecord.id,
+ };
+ } );
+
+ if ( ! history || ( ! coreOverlay && ! overlay ) ) {
+ return null;
+ }
+
+ return (
+ <>
+
+
+ { () => (
+ <>
+
+ {
+ setAttributes( {
+ overlayId: newOverlayId,
+ } );
+ } }
+ choices={ overlayChoices }
+ disabled={ overlayChoices?.length === 0 }
+ />
+
+
+
+
+
+ >
+ ) }
+
+ >
+ );
+}
diff --git a/packages/block-library/src/navigation/edit/index.js b/packages/block-library/src/navigation/edit/index.js
index 3cacd814119e6..0bdf01b27f891 100644
--- a/packages/block-library/src/navigation/edit/index.js
+++ b/packages/block-library/src/navigation/edit/index.js
@@ -35,6 +35,7 @@ import {
ToggleControl,
__experimentalToggleGroupControl as ToggleGroupControl,
__experimentalToggleGroupControlOption as ToggleGroupControlOption,
+ __experimentalHStack as HStack,
Button,
Spinner,
Notice,
@@ -43,6 +44,7 @@ import { __, sprintf } from '@wordpress/i18n';
import { speak } from '@wordpress/a11y';
import { close, Icon } from '@wordpress/icons';
import { useInstanceId } from '@wordpress/compose';
+import { privateApis as routerPrivateApis } from '@wordpress/router';
/**
* Internal dependencies
@@ -72,6 +74,20 @@ import DeletedNavigationWarning from './deleted-navigation-warning';
import AccessibleDescription from './accessible-description';
import AccessibleMenuDescription from './accessible-menu-description';
import { unlock } from '../../lock-unlock';
+import EditOverlayButton from './edit-overlay-button';
+import useIsWithinOverlay from './use-is-within-overlay';
+import useGoToOverlayEditor from './use-go-to-overlay-editor';
+import useOverlay from './use-overlay';
+
+const { useLocation } = unlock( routerPrivateApis );
+
+function useInheritedRef() {
+ const {
+ params: { myNavRef },
+ } = useLocation();
+
+ return myNavRef;
+}
function Navigation( {
attributes,
@@ -108,7 +124,13 @@ function Navigation( {
icon = 'handle',
} = attributes;
- const ref = attributes.ref;
+ const [ tempRef, setTempRef ] = useState( null );
+
+ const ref = attributes.ref || tempRef;
+
+ const inheritedRef = useInheritedRef();
+
+ const isInheritRefMode = !! inheritedRef;
const setRef = useCallback(
( postId ) => {
@@ -122,6 +144,15 @@ function Navigation( {
const blockEditingMode = useBlockEditingMode();
+ const isInsideOverlay = useIsWithinOverlay();
+
+ const showOverlayControls = ! isInsideOverlay;
+
+ const customOverlay = useOverlay( attributes?.overlayId );
+ const goToOverlayEditor = useGoToOverlayEditor();
+
+ const hasCustomOverlay = !! customOverlay;
+
// Preload classic menus, so that they don't suddenly pop-in when viewing
// the Select Menu dropdown.
const { menus: classicMenus } = useNavigationEntities();
@@ -233,11 +264,21 @@ function Navigation( {
: null;
useEffect( () => {
+ // Todo: set the ref based on context.
+ if ( isInheritRefMode ) {
+ setTempRef( inheritedRef );
+ return;
+ }
+
// If:
// - there is an existing menu, OR
// - there are existing (uncontrolled) inner blocks
// ...then don't request a fallback menu.
- if ( ref || hasUnsavedBlocks || ! navigationFallbackId ) {
+ if (
+ ( ref && ! isInheritRefMode ) ||
+ hasUnsavedBlocks ||
+ ! navigationFallbackId
+ ) {
return;
}
@@ -255,6 +296,8 @@ function Navigation( {
hasUnsavedBlocks,
navigationFallbackId,
__unstableMarkNextChangeAsNotPersistent,
+ isInheritRefMode,
+ inheritedRef,
] );
const navRef = useRef();
@@ -358,6 +401,17 @@ function Navigation( {
handleUpdateMenu( menuId );
};
+ const onToggleOverlayMenu = ( _toggleVal ) => {
+ if ( hasCustomOverlay && _toggleVal ) {
+ // If there is a Custom Overlay and the user is trying to open the menu
+ // then edit the overlay template part.
+ goToOverlayEditor( customOverlay?.id, ref );
+ } else {
+ // Otherwise just toggle the default overlay witin the editor.
+ setResponsiveMenuVisibility( _toggleVal );
+ }
+ };
+
useEffect( () => {
hideNavigationMenuStatusNotice();
@@ -528,7 +582,7 @@ function Navigation( {
{ hasSubmenuIndicatorSetting && (
- { isResponsive && (
+ { isResponsive && showOverlayControls && (
<>
diff --git a/packages/block-library/src/navigation/edit/use-go-to-overlay-editor.js b/packages/block-library/src/navigation/edit/use-go-to-overlay-editor.js
new file mode 100644
index 0000000000000..bf0f612071243
--- /dev/null
+++ b/packages/block-library/src/navigation/edit/use-go-to-overlay-editor.js
@@ -0,0 +1,26 @@
+/**
+ * WordPress dependencies
+ */
+import { privateApis as routerPrivateApis } from '@wordpress/router';
+
+/**
+ * Internal dependencies
+ */
+import { unlock } from '../../lock-unlock';
+
+const { useHistory } = unlock( routerPrivateApis );
+
+export default function useGoToOverlayEditor() {
+ const history = useHistory();
+
+ function goToOverlayEditor( overlayId, navRef ) {
+ history.push( {
+ postId: overlayId,
+ postType: 'wp_template_part',
+ canvas: 'edit',
+ myNavRef: navRef,
+ } );
+ }
+
+ return goToOverlayEditor;
+}
diff --git a/packages/block-library/src/navigation/edit/use-is-within-overlay.js b/packages/block-library/src/navigation/edit/use-is-within-overlay.js
new file mode 100644
index 0000000000000..ab3594998ddbf
--- /dev/null
+++ b/packages/block-library/src/navigation/edit/use-is-within-overlay.js
@@ -0,0 +1,7 @@
+import { useEntityProp } from '@wordpress/core-data';
+import { NAVIGATION_OVERLAY_TEMPLATE_PART_AREA } from '../constants';
+
+export default function useIsWithinOverlay() {
+ const [ area ] = useEntityProp( 'postType', 'wp_template_part', 'area' );
+ return area === NAVIGATION_OVERLAY_TEMPLATE_PART_AREA;
+}
diff --git a/packages/block-library/src/navigation/edit/use-overlay.js b/packages/block-library/src/navigation/edit/use-overlay.js
new file mode 100644
index 0000000000000..c91a30addf4ab
--- /dev/null
+++ b/packages/block-library/src/navigation/edit/use-overlay.js
@@ -0,0 +1,32 @@
+/**
+ * WordPress dependencies
+ */
+import { store as coreStore } from '@wordpress/core-data';
+import { useSelect } from '@wordpress/data';
+
+export default function useOverlay( currentOverlayId ) {
+ return useSelect(
+ ( select ) => {
+ const themeSlug = select( coreStore ).getCurrentTheme()?.stylesheet;
+
+ const themeOverlay = themeSlug
+ ? select( coreStore ).getEntityRecord(
+ 'postType',
+ 'wp_template_part',
+ `${ themeSlug }//navigation-overlay`
+ )
+ : null;
+
+ const customOverlay = themeSlug
+ ? select( coreStore ).getEntityRecord(
+ 'postType',
+ 'wp_template_part',
+ currentOverlayId
+ )
+ : null;
+
+ return customOverlay || themeOverlay;
+ },
+ [ currentOverlayId ]
+ );
+}
diff --git a/packages/block-library/src/navigation/editor.scss b/packages/block-library/src/navigation/editor.scss
index 107fb6e6de5fd..f4357b9e5c7ad 100644
--- a/packages/block-library/src/navigation/editor.scss
+++ b/packages/block-library/src/navigation/editor.scss
@@ -193,6 +193,15 @@ $color-control-label-height: 20px;
}
}
+// Overlay UI controls
+.wp-block-navigation__edit-overlay-button {
+ text-transform: uppercase;
+ font-size: 11px;
+ &.is-link {
+ text-decoration: none;
+ }
+}
+
// Override inner padding on default appender.
// This should be a temporary fix, to be replaced by improvements to
// the sibling inserter. Or by the dropdown appender that is used in Group, instead of the "Button block appender".
@@ -645,3 +654,11 @@ body.editor-styles-wrapper .wp-block-navigation__responsive-container.is-menu-op
.wp-block-navigation__menu-inspector-controls__empty-message {
margin-left: 24px;
}
+
+.wp-block-navigation__menu-inspector-controls__overlay-menu {
+ margin-bottom: $grid-unit-20;
+
+ .wp-block-navigation__menu-inspector-controls__overlay-menu-heading {
+ margin-bottom: 0;
+ }
+}
diff --git a/packages/block-library/src/navigation/style.scss b/packages/block-library/src/navigation/style.scss
index 0b70ebb656cfa..581b0cba8cff8 100644
--- a/packages/block-library/src/navigation/style.scss
+++ b/packages/block-library/src/navigation/style.scss
@@ -510,12 +510,14 @@ button.wp-block-navigation-item__content {
animation-fill-mode: forwards;
@include reduce-motion("animation");
+
// Try to inherit any root paddings set, so the X can align to a top-right aligned menu.
padding-top: clamp(1rem, var(--wp--style--root--padding-top), 20rem);
padding-right: clamp(1rem, var(--wp--style--root--padding-right), 20rem);
padding-bottom: clamp(1rem, var(--wp--style--root--padding-bottom), 20rem);
padding-left: clamp(1rem, var(--wp--style--root--padding-left), 20em);
+
// Allow modal to scroll.
overflow: auto;
@@ -635,6 +637,38 @@ button.wp-block-navigation-item__content {
}
}
+
+// Custom Overlay Overides
+// Removes any default styles from the overlay and its containers to afford the abilityt
+// for blocks within the editor to control the "styling" of the overlay.
+.wp-block-navigation__responsive-container.has-custom-overlay.is-menu-open {
+ padding: 0;
+
+ // Allow overlay to occupy 100% of the available vertical space.
+ &,
+ .wp-block-navigation__responsive-close,
+ .wp-block-navigation__responsive-dialog,
+ .wp-block-navigation__responsive-container-content,
+ .wp-block-navigation__overlay {
+ display: flex;
+ flex-direction: column;
+ flex-grow: 1;
+ }
+
+ .wp-block-navigation__overlay > .wp-block-group {
+ flex-grow: 1; // force first block to occupy all the space.
+ }
+
+ .wp-block-navigation__responsive-close {
+ max-width: none;
+ }
+
+ .wp-block-navigation__responsive-container-content {
+ padding-top: 0;
+ }
+}
+
+
// Default menu background and font color.
.wp-block-navigation:not(.has-background)
.wp-block-navigation__responsive-container.is-menu-open {
@@ -741,7 +775,7 @@ button.wp-block-navigation-item__content {
// Adjust open dialog top margin when admin-bar is visible.
// Needs to be scoped to .is-menu-open, or it will shift the position of any other navigations that may be present.
-.has-modal-open .admin-bar .is-menu-open .wp-block-navigation__responsive-dialog {
+.has-modal-open .admin-bar .is-menu-open .wp-block-navigation__responsive-container:not(.has-custom-overlay) .wp-block-navigation__responsive-dialog {
margin-top: $admin-bar-height-big;
// Handle smaller admin-bar.
@@ -754,3 +788,20 @@ button.wp-block-navigation-item__content {
html.has-modal-open {
overflow: hidden;
}
+
+
+.wp-block-navigation__overlay {
+ display: none;
+ width: 100%; // fill the overlay
+ height: 100%; // fill the overlay
+}
+
+.is-menu-open.has-custom-overlay {
+ .wp-block-navigation__overlay {
+ display: initial;
+ }
+
+ .wp-block-navigation__default {
+ display: none;
+ }
+}
diff --git a/packages/block-library/src/style.scss b/packages/block-library/src/style.scss
index 790e09535f4b6..3cd119deb842e 100644
--- a/packages/block-library/src/style.scss
+++ b/packages/block-library/src/style.scss
@@ -25,6 +25,7 @@
@import "./media-text/style.scss";
@import "./navigation/style.scss";
@import "./navigation-link/style.scss";
+@import "./navigation-overlay-close/style.scss";
@import "./page-list/style.scss";
@import "./paragraph/style.scss";
@import "./post-author/style.scss";
diff --git a/packages/edit-site/src/components/block-editor/editor-canvas.js b/packages/edit-site/src/components/block-editor/editor-canvas.js
index 01bc4cdfa2ddf..c6f4d316edcd1 100644
--- a/packages/edit-site/src/components/block-editor/editor-canvas.js
+++ b/packages/edit-site/src/components/block-editor/editor-canvas.js
@@ -22,6 +22,7 @@ import {
FOCUSABLE_ENTITIES,
NAVIGATION_POST_TYPE,
} from '../../utils/constants';
+import useIsNavigationOverlay from './use-is-navigation-overlay';
const { EditorCanvas: EditorCanvasRoot } = unlock( editorPrivateApis );
@@ -45,6 +46,7 @@ function EditorCanvas( { enableResizing, settings, children, ...props } ) {
}, [] );
const { setCanvasMode } = unlock( useDispatch( editSiteStore ) );
const [ isFocused, setIsFocused ] = useState( false );
+ const isNavigationOverlayTemplate = useIsNavigationOverlay();
useEffect( () => {
if ( canvasMode === 'edit' ) {
@@ -72,9 +74,12 @@ function EditorCanvas( { enableResizing, settings, children, ...props } ) {
const isNavigationFocusMode = isTemplateTypeNavigation && isFocusMode;
// Hide the appender when:
// - In navigation focus mode (should only allow the root Nav block).
+ // - editing the navigation overlay template.
// - In view mode (i.e. not editing).
const showBlockAppender =
- ( isNavigationFocusMode && hasBlocks ) || canvasMode === 'view'
+ ( isNavigationFocusMode && hasBlocks ) ||
+ isNavigationOverlayTemplate ||
+ canvasMode === 'view'
? false
: undefined;
diff --git a/packages/edit-site/src/components/block-editor/site-editor-canvas.js b/packages/edit-site/src/components/block-editor/site-editor-canvas.js
index f1e4a5fa3f2c6..dabfda67b45ca 100644
--- a/packages/edit-site/src/components/block-editor/site-editor-canvas.js
+++ b/packages/edit-site/src/components/block-editor/site-editor-canvas.js
@@ -23,6 +23,7 @@ import {
} from '../../utils/constants';
import { unlock } from '../../lock-unlock';
import { privateApis as routerPrivateApis } from '@wordpress/router';
+import useIsNavigationOverlay from './use-is-navigation-overlay';
const { useLocation } = unlock( routerPrivateApis );
@@ -45,6 +46,8 @@ export default function SiteEditorCanvas() {
[]
);
const isFocusMode = location.params.focusMode || isFocusableEntity;
+ const isNavigationOverlayTemplate = useIsNavigationOverlay();
+
const [ resizeObserver, sizes ] = useResizeObserver();
const settings = useSiteEditorSettings();
@@ -60,7 +63,9 @@ export default function SiteEditorCanvas() {
const isTemplateTypeNavigation = templateType === NAVIGATION_POST_TYPE;
const isNavigationFocusMode = isTemplateTypeNavigation && isFocusMode;
- const forceFullHeight = isNavigationFocusMode;
+
+ const forceFullHeight =
+ isNavigationFocusMode || isNavigationOverlayTemplate;
return (
diff --git a/packages/edit-site/src/components/block-editor/use-is-navigation-overlay.js b/packages/edit-site/src/components/block-editor/use-is-navigation-overlay.js
new file mode 100644
index 0000000000000..53bd7b19feec7
--- /dev/null
+++ b/packages/edit-site/src/components/block-editor/use-is-navigation-overlay.js
@@ -0,0 +1,9 @@
+/**
+ * WordPress dependencies
+ */
+import { useEntityProp } from '@wordpress/core-data';
+
+export default function useIsNavigationOverlay() {
+ const [ area ] = useEntityProp( 'postType', 'wp_template_part', 'area' );
+ return area === 'navigation-overlay';
+}