diff --git a/package-lock.json b/package-lock.json
index 2b506b15ae9a0..004a666df5ad3 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -15958,6 +15958,7 @@
"@wordpress/plugins": "file:packages/plugins",
"@wordpress/primitives": "file:packages/primitives",
"@wordpress/url": "file:packages/url",
+ "classnames": "^2.2.5",
"downloadjs": "^1.4.7",
"file-saver": "^2.0.2",
"jszip": "^3.2.2",
diff --git a/packages/edit-site/package.json b/packages/edit-site/package.json
index 7e90f03aafaa4..034609ad9f025 100644
--- a/packages/edit-site/package.json
+++ b/packages/edit-site/package.json
@@ -50,6 +50,7 @@
"@wordpress/plugins": "file:../plugins",
"@wordpress/primitives": "file:../primitives",
"@wordpress/url": "file:../url",
+ "classnames": "^2.2.5",
"downloadjs": "^1.4.7",
"file-saver": "^2.0.2",
"jszip": "^3.2.2",
diff --git a/packages/edit-site/src/components/header/document-actions/index.js b/packages/edit-site/src/components/header/document-actions/index.js
new file mode 100644
index 0000000000000..65d33f2147800
--- /dev/null
+++ b/packages/edit-site/src/components/header/document-actions/index.js
@@ -0,0 +1,152 @@
+/**
+ * External dependencies
+ */
+import classnames from 'classnames';
+
+/**
+ * WordPress dependencies
+ */
+import { Button, Dropdown } from '@wordpress/components';
+import { __ } from '@wordpress/i18n';
+import {
+ __experimentalGetBlockLabel as getBlockLabel,
+ getBlockType,
+} from '@wordpress/blocks';
+import { useDispatch, useSelect } from '@wordpress/data';
+import { DOWN } from '@wordpress/keycodes';
+
+function useSecondaryText() {
+ const selectedBlock = useSelect( ( select ) => {
+ return select( 'core/block-editor' ).getSelectedBlock();
+ } );
+
+ // TODO: Handle if parent is template part too.
+ const selectedBlockLabel =
+ selectedBlock?.name === 'core/template-part'
+ ? getBlockLabel(
+ getBlockType( selectedBlock?.name ),
+ selectedBlock?.attributes
+ )
+ : null;
+
+ if ( selectedBlockLabel ) {
+ return {
+ label: selectedBlockLabel,
+ isActive: true,
+ };
+ }
+ return {};
+}
+
+export default function DocumentActions( { documentTitle } ) {
+ const { label, isActive } = useSecondaryText();
+ // Title is active when there is no secondary item, or when the secondary
+ // item is inactive.
+ const isTitleActive = ! label?.length || ! isActive;
+
+ const { page } = useSelect( ( select ) => {
+ const { getPage } = select( 'core/edit-site' );
+ const { getEntityRecord } = select( 'core' );
+ const _page = getPage();
+ return {
+ page: getEntityRecord( 'postType', 'page', _page?.context?.postId ),
+ };
+ } );
+
+ const { editEntityRecord } = useDispatch( 'core' );
+ const editTitle = ( title ) => {
+ editEntityRecord( 'postType', 'page', page.id, {
+ title,
+ slug: title,
+ } );
+ };
+
+ return (
+
+ { documentTitle ? (
+ <>
+
{
+ const openOnArrowDown = ( event ) => {
+ if ( ! isOpen && event.keyCode === DOWN ) {
+ event.preventDefault();
+ event.stopPropagation();
+ onToggle();
+ }
+ };
+ {
+ /* TODO: Fix vertical text padding */
+ }
+ return (
+
+ );
+ } }
+ renderContent={ () => (
+
+ { /* TODO: Replace inline styles */ }
+
+ Name
+
+ { /* TODO: Don't allow input when there is no page context */ }
+ {
+ const REGEXP_NEWLINES = /[\r\n]+/g;
+ const title = event.target.value.replace(
+ REGEXP_NEWLINES,
+ ' '
+ );
+
+ editTitle( title );
+ } }
+ />
+
+ ) }
+ >
+
+ { label ?? '' }
+
+ >
+ ) : (
+ __( 'Loading…' )
+ ) }
+
+ );
+}
diff --git a/packages/edit-site/src/components/header/document-actions/style.scss b/packages/edit-site/src/components/header/document-actions/style.scss
new file mode 100644
index 0000000000000..e17ca10910ea4
--- /dev/null
+++ b/packages/edit-site/src/components/header/document-actions/style.scss
@@ -0,0 +1,34 @@
+.edit-site-document-actions {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: space-evenly;
+
+ .edit-site-document-actions__label {
+ color: $gray-700;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ transition: height 0.25s;
+
+ &.is-active {
+ color: inherit;
+ }
+
+ &.edit-site-document-actions__title {
+ height: 100%;
+ // Otherwise, the secondary item still takes up space with height 0:
+ flex-grow: 1;
+ }
+
+ &.edit-site-document-actions__secondary-item {
+ height: 0;
+ }
+ }
+
+ &.has-secondary-label {
+ .edit-site-document-actions__label {
+ height: 50%;
+ }
+ }
+}
diff --git a/packages/edit-site/src/components/header/index.js b/packages/edit-site/src/components/header/index.js
index 1dcdb84737876..563a60602dae7 100644
--- a/packages/edit-site/src/components/header/index.js
+++ b/packages/edit-site/src/components/header/index.js
@@ -27,6 +27,7 @@ import SaveButton from '../save-button';
import UndoButton from './undo-redo/undo';
import RedoButton from './undo-redo/redo';
import FullscreenModeClose from './fullscreen-mode-close';
+import DocumentActions from './document-actions';
export default function Header( {
openEntitiesSavedStates,
@@ -36,6 +37,7 @@ export default function Header( {
const {
deviceType,
hasFixedToolbar,
+ template,
templateId,
templatePartId,
templateType,
@@ -51,14 +53,18 @@ export default function Header( {
getPage,
} = select( 'core/edit-site' );
- const { show_on_front: _showOnFront } = select(
- 'core'
- ).getEditedEntityRecord( 'root', 'site' );
+ const { getEntityRecord, getEditedEntityRecord } = select( 'core' );
+ const { show_on_front: _showOnFront } = getEditedEntityRecord(
+ 'root',
+ 'site'
+ );
+ const _templateId = getTemplateId();
return {
deviceType: __experimentalGetPreviewDeviceType(),
hasFixedToolbar: isFeatureActive( 'fixedToolbar' ),
- templateId: getTemplateId(),
+ templateId: _templateId,
+ template: getEntityRecord( 'postType', 'wp_template', _templateId ),
templatePartId: getTemplatePartId(),
templateType: getTemplateType(),
page: getPage(),
@@ -81,62 +87,73 @@ export default function Header( {
return (
-
-
-
-
-