diff --git a/lib/compat/wordpress-7.0/rest-api.php b/lib/compat/wordpress-7.0/rest-api.php
index 75301fdac94d7d..ff5a757f1907f8 100644
--- a/lib/compat/wordpress-7.0/rest-api.php
+++ b/lib/compat/wordpress-7.0/rest-api.php
@@ -65,3 +65,71 @@ function gutenberg_parse_pattern_blocks_in_block_templates( $query_result, $quer
}
add_filter( 'get_block_templates', 'gutenberg_parse_pattern_blocks_in_block_templates', 10, 3 );
+
+/**
+ * Registers the 'overlay' template part area when the experiment is enabled.
+ *
+ * @param array $areas Array of template part area definitions.
+ * @return array Modified array of template part area definitions.
+ */
+function gutenberg_register_overlay_template_part_area( $areas ) {
+ if ( ! gutenberg_is_experiment_enabled( 'gutenberg-customizable-navigation-overlays' ) ) {
+ return $areas;
+ }
+
+ $areas[] = array(
+ 'area' => 'overlay',
+ 'label' => __( 'Overlay', 'gutenberg' ),
+ 'description' => __( 'Custom overlay area for navigation overlays.', 'gutenberg' ),
+ 'icon' => 'overlay',
+ 'area_tag' => 'div',
+ );
+
+ return $areas;
+}
+add_filter( 'default_wp_template_part_areas', 'gutenberg_register_overlay_template_part_area' );
+
+/**
+ * Registers the 'overlay' pattern category when the experiment is enabled.
+ */
+function gutenberg_register_overlay_pattern_category() {
+ if ( ! gutenberg_is_experiment_enabled( 'gutenberg-customizable-navigation-overlays' ) ) {
+ return;
+ }
+
+ register_block_pattern_category(
+ 'overlay',
+ array( 'label' => __( 'Overlay', 'gutenberg' ) )
+ );
+}
+add_action( 'init', 'gutenberg_register_overlay_pattern_category' );
+
+/**
+ * Registers the default overlay pattern when the experiment is enabled.
+ */
+function gutenberg_register_overlay_pattern() {
+ if ( ! gutenberg_is_experiment_enabled( 'gutenberg-customizable-navigation-overlays' ) ) {
+ return;
+ }
+
+ register_block_pattern(
+ 'gutenberg/overlay-default',
+ array(
+ 'title' => __( 'Overlay', 'gutenberg' ),
+ 'categories' => array( 'overlay' ),
+ 'blockTypes' => array( 'core/template-part/overlay' ),
+ 'content' => '
+
+',
+ )
+ );
+}
+add_action( 'init', 'gutenberg_register_overlay_pattern' );
diff --git a/packages/block-library/src/index.js b/packages/block-library/src/index.js
index e71d6d1b651ac7..29d5bea26ea4c1 100644
--- a/packages/block-library/src/index.js
+++ b/packages/block-library/src/index.js
@@ -82,6 +82,7 @@ import * as navigation from './navigation';
import * as navigationLink from './navigation-link';
import * as navigationSubmenu from './navigation-submenu';
import * as nextpage from './nextpage';
+import * as overlayClose from './overlay-close';
import * as pattern from './pattern';
import * as pageList from './page-list';
import * as pageListItem from './page-list-item';
@@ -187,6 +188,7 @@ const getAllBlocks = () => {
missing,
more,
nextpage,
+ overlayClose,
pageList,
pageListItem,
pattern,
diff --git a/packages/block-library/src/navigation/block.json b/packages/block-library/src/navigation/block.json
index 1315cce9318fbb..7ccb7578263cd2 100644
--- a/packages/block-library/src/navigation/block.json
+++ b/packages/block-library/src/navigation/block.json
@@ -84,6 +84,9 @@
"templateLock": {
"type": [ "string", "boolean" ],
"enum": [ "all", "insert", "contentOnly", false ]
+ },
+ "overlayTemplatePartId": {
+ "type": "string"
}
},
"providesContext": {
diff --git a/packages/block-library/src/navigation/edit/index.js b/packages/block-library/src/navigation/edit/index.js
index 74b29f2fc4a07d..62f0512748bb17 100644
--- a/packages/block-library/src/navigation/edit/index.js
+++ b/packages/block-library/src/navigation/edit/index.js
@@ -29,6 +29,7 @@ import {
BlockControls,
} from '@wordpress/block-editor';
import { EntityProvider, store as coreStore } from '@wordpress/core-data';
+import { store as editorStore } from '@wordpress/editor';
import { useDispatch, useSelect } from '@wordpress/data';
import {
@@ -43,6 +44,7 @@ import {
Notice,
ToolbarButton,
ToolbarGroup,
+ PanelBody,
} from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import { speak } from '@wordpress/a11y';
@@ -77,8 +79,11 @@ import MenuInspectorControls from './menu-inspector-controls';
import DeletedNavigationWarning from './deleted-navigation-warning';
import AccessibleDescription from './accessible-description';
import AccessibleMenuDescription from './accessible-menu-description';
+import OverlaySelector from './overlay-selector';
import { unlock } from '../../lock-unlock';
import { useToolsPanelDropdownMenuProps } from '../../utils/hooks';
+
+const TEMPLATE_PART_POST_TYPE = 'wp_template_part';
import { DEFAULT_BLOCK } from '../constants';
/**
@@ -262,7 +267,7 @@ function Navigation( {
} ) {
const {
openSubmenusOnClick,
- overlayMenu,
+ overlayMenu: savedOverlayMenu,
showSubmenuIcon,
templateLock,
layout: {
@@ -272,8 +277,37 @@ function Navigation( {
} = {},
hasIcon,
icon = 'handle',
+ overlayTemplatePartId: savedOverlayTemplatePartId,
} = attributes;
+ // Check if we're inside an overlay template part
+ const isInOverlayTemplatePart = useSelect( ( select ) => {
+ const { getCurrentPostType, getCurrentPostId } = unlock(
+ select( editorStore )
+ );
+ const { getEditedEntityRecord } = select( coreStore );
+ const postType = getCurrentPostType();
+ const postId = getCurrentPostId();
+
+ if ( postType !== TEMPLATE_PART_POST_TYPE || ! postId ) {
+ return false;
+ }
+
+ const templatePart = getEditedEntityRecord(
+ 'postType',
+ TEMPLATE_PART_POST_TYPE,
+ postId
+ );
+
+ return templatePart?.area === 'overlay';
+ }, [] );
+
+ // Ignore overlay attributes if we're inside an overlay template part
+ const overlayMenu = isInOverlayTemplatePart ? 'never' : savedOverlayMenu;
+ const overlayTemplatePartId = isInOverlayTemplatePart
+ ? undefined
+ : savedOverlayTemplatePartId;
+
const ref = attributes.ref;
const setRef = useCallback(
@@ -641,82 +675,91 @@ function Navigation( {
const dropdownMenuProps = useToolsPanelDropdownMenuProps();
+ // Get navigation function for overlay template parts
+ const onNavigateToEntityRecord = useSelect(
+ ( select ) =>
+ select( blockEditorStore ).getSettings().onNavigateToEntityRecord,
+ []
+ );
+
+ // Check if the experiment is enabled
+ const isExperimentEnabled =
+ typeof window !== 'undefined' &&
+ window.__experimentalNavigationOverlays;
+
const stylingInspectorControls = (
<>
-
- { hasSubmenuIndicatorSetting && (
- {
- setAttributes( {
- showSubmenuIcon: true,
- openSubmenusOnClick: false,
- overlayMenu: 'mobile',
- hasIcon: true,
- icon: 'handle',
- } );
- } }
- dropdownMenuProps={ dropdownMenuProps }
- >
- { isResponsive && (
- <>
-
- { overlayMenuPreview && (
-
+ ) }
+ { isResponsive && (
+ <>
+
- ) }
- >
- ) }
+ { hasIcon && (
+ <>
+
+
+ >
+ ) }
+ { ! hasIcon && (
+ <>
+ { __( 'Menu' ) }
+ { __( 'Close' ) }
+ >
+ ) }
+
+ { overlayMenuPreview && (
+
+
+
+ ) }
+ >
+ ) }
- overlayMenu !== 'mobile' }
- label={ __( 'Overlay Menu' ) }
- onDeselect={ () =>
- setAttributes( { overlayMenu: 'mobile' } )
- }
- isShownByDefault
- >
-
+ { /*
+ * Hide custom overlay controls when overlay visibility is "Off".
+ * Attributes are preserved (not modified) so that if the user
+ * toggles back to "Mobile" or "Always", their configured overlay
+ * will still be available.
+ *
+ * Note: PHP rendering code will need to account for this situation
+ * and should not render custom overlays when overlayMenu is "never",
+ * even if overlayTemplatePartId is set.
+ */ }
+ { overlayMenu !== 'never' && (
+ {
+ setAttributes( {
+ overlayTemplatePartId: newValue,
+ } );
+ } }
+ />
+ ) }
+
+
+
+ ) }
+
+ { hasSubmenuIndicatorSetting && (
+ {
+ setAttributes( {
+ showSubmenuIcon: true,
+ openSubmenusOnClick: false,
+ hasIcon: true,
+ icon: 'handle',
+ } );
+ } }
+ dropdownMenuProps={ dropdownMenuProps }
+ >
{ hasSubmenus && (
<>
@@ -880,6 +960,8 @@ function Navigation( {
isHiddenByDefault={ isHiddenByDefault }
overlayBackgroundColor={ overlayBackgroundColor }
overlayTextColor={ overlayTextColor }
+ overlayTemplatePartId={ overlayTemplatePartId }
+ onNavigateToEntityRecord={ onNavigateToEntityRecord }
>
{ isEntityAvailable && (
+ select( blockEditorStore ).getSettings().onNavigateToEntityRecord,
+ []
+ );
+
+ const overlayParts =
+ templateParts?.filter( ( part ) => part.area === 'overlay' ) || [];
+
+ const options = [
+ { label: __( 'None' ), value: '' },
+ ...overlayParts.map( ( part ) => {
+ const templatePartId = createTemplatePartId(
+ part.theme,
+ part.slug
+ );
+ return {
+ label: part.title?.rendered || part.slug || __( 'Untitled' ),
+ value: templatePartId,
+ };
+ } ),
+ ];
+
+ const handleEditClick = () => {
+ if ( value && onNavigateToEntityRecord ) {
+ onNavigateToEntityRecord( {
+ postId: value,
+ postType: 'wp_template_part',
+ } );
+ }
+ };
+
+ const handleCreateNew = async () => {
+ setIsCreating( true );
+
+ // Generate a unique title by checking existing overlay template parts
+ const baseTitle = __( 'Overlay' );
+ const existingTitles = overlayParts.map(
+ ( part ) => part.title?.rendered || ''
+ );
+
+ // Find the next available number
+ let titleNumber = 1;
+ let uniqueTitle = baseTitle;
+ while ( existingTitles.includes( uniqueTitle ) ) {
+ titleNumber++;
+ uniqueTitle = `${ baseTitle } ${ titleNumber }`;
+ }
+
+ const cleanSlug =
+ kebabCase( uniqueTitle ).replace( /[^\w-]+/g, '' ) ||
+ 'wp-custom-overlay';
+
+ try {
+ const templatePart = await saveEntityRecord(
+ 'postType',
+ 'wp_template_part',
+ {
+ title: uniqueTitle,
+ slug: cleanSlug,
+ content: serialize( [] ),
+ area: 'overlay',
+ },
+ { throwOnError: true }
+ );
+
+ // Create the proper template part ID format (theme//slug)
+ const templatePartId = createTemplatePartId(
+ templatePart.theme,
+ templatePart.slug
+ );
+
+ // Set the new template part as the selected overlay
+ onChange( templatePartId );
+
+ // Navigate to the new template part for editing
+ if ( onNavigateToEntityRecord && templatePartId ) {
+ onNavigateToEntityRecord( {
+ postId: templatePartId,
+ postType: 'wp_template_part',
+ } );
+ }
+ } catch ( error ) {
+ console.error( 'Failed to create overlay template part:', error );
+ } finally {
+ setIsCreating( false );
+ }
+ };
+
+ // Get the selected template part entity
+ const selectedTemplatePart = useSelect(
+ ( select ) => {
+ if ( ! value ) {
+ return null;
+ }
+ const { getEditedEntityRecord } = select( coreStore );
+ try {
+ return getEditedEntityRecord(
+ 'postType',
+ 'wp_template_part',
+ value
+ );
+ } catch {
+ return null;
+ }
+ },
+ [ value ]
+ );
+
+ // Parse template part content to blocks for preview
+ const previewBlocks = useMemo( () => {
+ if ( ! selectedTemplatePart ) {
+ return null;
+ }
+ // Content can be either a string directly or content.raw
+ const contentString =
+ selectedTemplatePart.content?.raw || selectedTemplatePart.content;
+ if ( ! contentString || typeof contentString !== 'string' ) {
+ return null;
+ }
+ try {
+ const blocks = parse( contentString );
+ // Filter out null blocks that parse can return
+ const validBlocks = blocks?.filter( Boolean ) || [];
+ return validBlocks.length > 0 ? validBlocks : null;
+ } catch ( error ) {
+ console.error( 'Error parsing blocks:', error );
+ return null;
+ }
+ }, [ selectedTemplatePart ] );
+
+ // Check if we can edit (value exists, template part exists, and navigation is available)
+ const canEdit =
+ value &&
+ selectedTemplatePart &&
+ onNavigateToEntityRecord &&
+ ! isCreating;
+
+ return (
+
+ { value &&
+ ! isCreating &&
+ previewBlocks &&
+ previewBlocks.length > 0 && (
+
+
+ {
+ if (
+ ( event.key === 'Enter' ||
+ event.key === ' ' ) &&
+ canEdit
+ ) {
+ event.preventDefault();
+ handleEditClick();
+ }
+ } }
+ aria-label={ __( 'Edit overlay' ) }
+ >
+
+
+
+
+
+
+ ) }
+ {
+ onChange( newValue === '' ? undefined : newValue );
+ } }
+ disabled={ isCreating }
+ help={
+ <>
+ { __(
+ 'Select a template part to use as the custom overlay or '
+ ) }
+
+ .
+ >
+ }
+ />
+
+
+ );
+}
diff --git a/packages/block-library/src/navigation/edit/responsive-wrapper.js b/packages/block-library/src/navigation/edit/responsive-wrapper.js
index 886e808965ce4b..e3785641098bbd 100644
--- a/packages/block-library/src/navigation/edit/responsive-wrapper.js
+++ b/packages/block-library/src/navigation/edit/responsive-wrapper.js
@@ -27,6 +27,8 @@ export default function ResponsiveWrapper( {
overlayTextColor,
hasIcon,
icon,
+ overlayTemplatePartId,
+ onNavigateToEntityRecord,
} ) {
if ( ! isResponsive ) {
return children;
@@ -75,6 +77,18 @@ export default function ResponsiveWrapper( {
} ),
};
+ const handleOpenClick = () => {
+ // If there's a custom overlay template part, navigate to it
+ if ( overlayTemplatePartId && onNavigateToEntityRecord && ! isOpen ) {
+ onNavigateToEntityRecord( {
+ postId: overlayTemplatePartId,
+ postType: 'wp_template_part',
+ } );
+ } else {
+ onToggle( true );
+ }
+ };
+
return (
<>
{ ! isOpen && (
@@ -83,7 +97,7 @@ export default function ResponsiveWrapper( {
aria-haspopup="true"
aria-label={ hasIcon && __( 'Open menu' ) }
className={ openButtonClasses }
- onClick={ () => onToggle( true ) }
+ onClick={ handleOpenClick }
>
{ hasIcon && }
{ ! hasIcon && __( 'Menu' ) }
diff --git a/packages/block-library/src/overlay-close/block.json b/packages/block-library/src/overlay-close/block.json
new file mode 100644
index 00000000000000..d7dc325a9916b4
--- /dev/null
+++ b/packages/block-library/src/overlay-close/block.json
@@ -0,0 +1,33 @@
+{
+ "$schema": "https://schemas.wp.org/trunk/block.json",
+ "apiVersion": 3,
+ "name": "core/overlay-close",
+ "title": "Overlay Close",
+ "category": "design",
+ "description": "A close button for navigation overlays.",
+ "textdomain": "default",
+ "attributes": {
+ "displayMode": {
+ "type": "string",
+ "default": "icon"
+ }
+ },
+ "supports": {
+ "html": false,
+ "anchor": true,
+ "color": {
+ "gradients": true,
+ "__experimentalDefaultControls": {
+ "background": true,
+ "text": true
+ }
+ },
+ "spacing": {
+ "margin": true,
+ "padding": true
+ }
+ },
+ "editorStyle": "wp-block-overlay-close-editor",
+ "style": "wp-block-overlay-close"
+}
+
diff --git a/packages/block-library/src/overlay-close/edit.js b/packages/block-library/src/overlay-close/edit.js
new file mode 100644
index 00000000000000..648dc6d06829d7
--- /dev/null
+++ b/packages/block-library/src/overlay-close/edit.js
@@ -0,0 +1,81 @@
+/**
+ * External dependencies
+ */
+import clsx from 'clsx';
+
+/**
+ * WordPress dependencies
+ */
+import {
+ InspectorControls,
+ useBlockProps,
+ __experimentalUseColorProps as useColorProps,
+} from '@wordpress/block-editor';
+import {
+ Button,
+ PanelBody,
+ __experimentalToggleGroupControl as ToggleGroupControl,
+ __experimentalToggleGroupControlOption as ToggleGroupControlOption,
+} from '@wordpress/components';
+import { close } from '@wordpress/icons';
+import { __ } from '@wordpress/i18n';
+
+export default function OverlayCloseEdit( { attributes, setAttributes } ) {
+ const { displayMode = 'icon' } = attributes;
+ const colorProps = useColorProps( attributes );
+ const blockProps = useBlockProps( {
+ className: 'wp-block-overlay-close',
+ style: {
+ ...colorProps.style,
+ },
+ } );
+
+ const showIcon = displayMode === 'icon' || displayMode === 'both';
+ const showText = displayMode === 'text' || displayMode === 'both';
+
+ return (
+ <>
+
+
+
+ setAttributes( { displayMode: value } )
+ }
+ isBlock
+ >
+
+
+
+
+
+
+
+
+
+ >
+ );
+}
diff --git a/packages/block-library/src/overlay-close/editor.scss b/packages/block-library/src/overlay-close/editor.scss
new file mode 100644
index 00000000000000..82d2760bd180b3
--- /dev/null
+++ b/packages/block-library/src/overlay-close/editor.scss
@@ -0,0 +1,17 @@
+.wp-block-overlay-close {
+ .wp-block-overlay-close__button {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 0.5rem;
+ background: transparent;
+ border: none;
+ cursor: pointer;
+ color: currentColor;
+
+ &:hover {
+ opacity: 0.8;
+ }
+ }
+}
+
diff --git a/packages/block-library/src/overlay-close/index.js b/packages/block-library/src/overlay-close/index.js
new file mode 100644
index 00000000000000..bc3359bd7d1934
--- /dev/null
+++ b/packages/block-library/src/overlay-close/index.js
@@ -0,0 +1,63 @@
+/**
+ * WordPress dependencies
+ */
+import { addFilter } from '@wordpress/hooks';
+import { close } from '@wordpress/icons';
+import { select as dataSelect } from '@wordpress/data';
+import { store as editorStore } from '@wordpress/editor';
+import { store as coreStore } from '@wordpress/core-data';
+import { unlock } from '../lock-unlock';
+
+/**
+ * Internal dependencies
+ */
+import initBlock from '../utils/init-block';
+import metadata from './block.json';
+import edit from './edit';
+import save from './save';
+
+const { name } = metadata;
+const TEMPLATE_PART_POST_TYPE = 'wp_template_part';
+
+export { metadata, name };
+
+export const settings = {
+ icon: close,
+ edit,
+ save,
+};
+
+export const init = () => {
+ initBlock( { name, metadata, settings } );
+
+ // Restrict overlay-close block to only overlay template parts
+ addFilter(
+ 'blockEditor.__unstableCanInsertBlockType',
+ 'core/overlay-close/restrict-to-overlay',
+ ( canInsert, blockType ) => {
+ if ( blockType.name !== 'core/overlay-close' ) {
+ return canInsert;
+ }
+
+ // Check if we're in an overlay template part
+ const { getCurrentPostType, getCurrentPostId } = unlock(
+ dataSelect( editorStore )
+ );
+ const { getEditedEntityRecord } = dataSelect( coreStore );
+ const postType = getCurrentPostType();
+ const postId = getCurrentPostId();
+
+ if ( postType !== TEMPLATE_PART_POST_TYPE || ! postId ) {
+ return false;
+ }
+
+ const templatePart = getEditedEntityRecord(
+ 'postType',
+ TEMPLATE_PART_POST_TYPE,
+ postId
+ );
+
+ return templatePart?.area === 'overlay';
+ }
+ );
+};
diff --git a/packages/block-library/src/overlay-close/save.js b/packages/block-library/src/overlay-close/save.js
new file mode 100644
index 00000000000000..ce15f4e152fc41
--- /dev/null
+++ b/packages/block-library/src/overlay-close/save.js
@@ -0,0 +1,45 @@
+/**
+ * External dependencies
+ */
+import clsx from 'clsx';
+
+/**
+ * WordPress dependencies
+ */
+import {
+ useBlockProps,
+ __experimentalGetColorClassesAndStyles as getColorClassesAndStyles,
+} from '@wordpress/block-editor';
+
+export default function OverlayCloseSave( { attributes, className } ) {
+ const { displayMode = 'icon' } = attributes;
+ const colorProps = getColorClassesAndStyles( attributes );
+
+ const showIcon = displayMode === 'icon' || displayMode === 'both';
+ const showText = displayMode === 'text' || displayMode === 'both';
+
+ return (
+
+
+
+ );
+}
diff --git a/packages/block-library/src/overlay-close/style.scss b/packages/block-library/src/overlay-close/style.scss
new file mode 100644
index 00000000000000..7a6853f5b64984
--- /dev/null
+++ b/packages/block-library/src/overlay-close/style.scss
@@ -0,0 +1,26 @@
+.wp-block-overlay-close {
+ .wp-block-overlay-close__button {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 0.5rem;
+ background: transparent;
+ border: none;
+ cursor: pointer;
+ color: currentColor;
+
+ &:hover {
+ opacity: 0.8;
+ }
+ }
+
+ .wp-block-overlay-close__icon {
+ font-size: 1.5rem;
+ line-height: 1;
+ }
+
+ .wp-block-overlay-close__text {
+ margin-left: 0.5rem;
+ }
+}
+
diff --git a/packages/block-library/src/overlay-close/use-template-part-area.js b/packages/block-library/src/overlay-close/use-template-part-area.js
new file mode 100644
index 00000000000000..5dc5a79c2a34d6
--- /dev/null
+++ b/packages/block-library/src/overlay-close/use-template-part-area.js
@@ -0,0 +1,37 @@
+/**
+ * WordPress dependencies
+ */
+import { useSelect } from '@wordpress/data';
+import { store as editorStore } from '@wordpress/editor';
+import { store as coreStore } from '@wordpress/core-data';
+import { unlock } from '../lock-unlock';
+
+const TEMPLATE_PART_POST_TYPE = 'wp_template_part';
+
+/**
+ * Hook to check if we're currently editing an overlay template part.
+ *
+ * @return {boolean} True if editing an overlay template part, false otherwise.
+ */
+export function useIsOverlayTemplatePart() {
+ return useSelect( ( select ) => {
+ const { getCurrentPostType, getCurrentPostId } = unlock(
+ select( editorStore )
+ );
+ const { getEditedEntityRecord } = select( coreStore );
+ const postType = getCurrentPostType();
+ const postId = getCurrentPostId();
+
+ if ( postType !== TEMPLATE_PART_POST_TYPE || ! postId ) {
+ return false;
+ }
+
+ const templatePart = getEditedEntityRecord(
+ 'postType',
+ TEMPLATE_PART_POST_TYPE,
+ postId
+ );
+
+ return templatePart?.area === 'overlay';
+ }, [] );
+}
diff --git a/packages/editor/src/components/visual-editor/index.js b/packages/editor/src/components/visual-editor/index.js
index 9c2d1326394cf5..12f3dd3a7b5044 100644
--- a/packages/editor/src/components/visual-editor/index.js
+++ b/packages/editor/src/components/visual-editor/index.js
@@ -117,6 +117,7 @@ function VisualEditor( {
isPreview,
styles,
canvasMinHeight,
+ isOverlayTemplatePart,
} = useSelect( ( select ) => {
const {
getCurrentPostId,
@@ -150,6 +151,20 @@ function VisualEditor( {
)
: undefined;
+ // Check if we're editing an overlay template part
+ let _isOverlayTemplatePart = false;
+ if ( postTypeSlug === TEMPLATE_PART_POST_TYPE ) {
+ const currentPostId = getCurrentPostId();
+ const templatePart = currentPostId
+ ? getEditedEntityRecord(
+ 'postType',
+ TEMPLATE_PART_POST_TYPE,
+ currentPostId
+ )
+ : null;
+ _isOverlayTemplatePart = templatePart?.area === 'overlay';
+ }
+
return {
renderingMode: _renderingMode,
postContentAttributes: editorSettings.postContentAttributes,
@@ -168,6 +183,7 @@ function VisualEditor( {
isPreview: editorSettings.isPreviewMode,
styles: editorSettings.styles,
canvasMinHeight: getCanvasMinHeight(),
+ isOverlayTemplatePart: _isOverlayTemplatePart,
};
}, [] );
const { isCleanNewPost } = useSelect( editorStore );
@@ -357,6 +373,11 @@ function VisualEditor( {
);
const iframeStyles = useMemo( () => {
+ // Full-height styles for overlay template parts
+ const overlayFullHeightCSS = isOverlayTemplatePart
+ ? `.block-editor-iframe__html{height:100vh;overflow:hidden;}.block-editor-iframe__body{height:100vh;min-height:100vh;display:flex;flex-direction:column;}.is-root-container{min-height:100vh;height:100%;flex:1;display:flex;flex-direction:column;}`
+ : '';
+
return [
...( styles ?? [] ),
{
@@ -377,12 +398,19 @@ function VisualEditor( {
enableResizing
? `.block-editor-iframe__html{background:var(--wp-editor-canvas-background);display:flex;align-items:center;justify-content:center;min-height:100vh;}.block-editor-iframe__body{width:100%;}`
: ''
- }`,
+ }
+ ${ overlayFullHeightCSS }`,
// The CSS above centers the body content vertically when resizing is enabled and applies a background
// color to the iframe HTML element to match the background color of the editor canvas.
},
];
- }, [ styles, enableResizing, calculatedMinHeight, paddingStyle ] );
+ }, [
+ styles,
+ enableResizing,
+ calculatedMinHeight,
+ paddingStyle,
+ isOverlayTemplatePart,
+ ] );
const typewriterRef = useTypewriter();
contentRef = useMergeRefs( [
@@ -411,6 +439,7 @@ function VisualEditor( {
'has-padding': isFocusedEntity || enableResizing,
'is-resizable': enableResizing,
'is-iframed': ! disableIframe,
+ 'is-overlay-template-part': isOverlayTemplatePart,
}
) }
>