-
+
+
{ ( isWideViewport || ! showIconLabels ) && (
<>
{ isLargeViewport && (
@@ -152,6 +99,7 @@ function HeaderToolbar() {
variant={ showIconLabels ? 'tertiary' : undefined }
/>
{ overflowItems }
+ { hasSelectedBlocks && }
>
) }
@@ -159,4 +107,4 @@ function HeaderToolbar() {
);
}
-export default HeaderToolbar;
+export default forwardRef( HeaderToolbar );
diff --git a/packages/edit-post/src/components/header/header-toolbar/style.scss b/packages/edit-post/src/components/header/header-toolbar/style.scss
index 87aec00004c02..577e244b53efd 100644
--- a/packages/edit-post/src/components/header/header-toolbar/style.scss
+++ b/packages/edit-post/src/components/header/header-toolbar/style.scss
@@ -1,11 +1,11 @@
-.edit-post-header-toolbar {
+.edit-post-header-document-toolbar {
display: inline-flex;
flex-grow: 1;
align-items: center;
border: none;
// Hide all action buttons except the inserter on mobile.
- .edit-post-header-toolbar__left > .components-button {
+ .edit-post-header-document-toolbar__left > .components-button {
display: none;
@include break-small() {
@@ -13,7 +13,7 @@
}
}
- .edit-post-header-toolbar__left > .edit-post-header-toolbar__inserter-toggle {
+ .edit-post-header-document-toolbar__left > .edit-post-header-toolbar__inserter-toggle {
display: inline-flex;
svg {
@@ -39,11 +39,94 @@
// The Toolbar component adds different styles to buttons, so we reset them
// here to the original button styles
- .edit-post-header-toolbar__left > .components-button.has-icon,
- .edit-post-header-toolbar__left > .components-dropdown > .components-button.has-icon {
+ .edit-post-header-document-toolbar__left > .components-button.has-icon:first-child {
+ margin-right: $grid-unit-10;
+ }
+
+ .edit-post-header-document-toolbar__left > .components-button.has-icon,
+ .edit-post-header-document-toolbar__left > .components-dropdown > .components-button.has-icon {
+ height: $grid-unit-40;
+ min-width: $grid-unit-40;
+ margin-right: $grid-unit-05;
+ padding: 0;
+ width: 32px;
+
+ &.is-pressed {
+ background: $gray-900;
+ }
+
+ &:focus:not(:disabled) {
+ box-shadow: 0 0 0 var(--wp-admin-border-width-focus) var(--wp-admin-theme-color), inset 0 0 0 $border-width $white;
+ outline: 1px solid transparent;
+ }
+
+ &::before {
+ display: none;
+ }
+ }
+
+ & > .edit-post-header-document-toolbar__left button.components-button.edit-post-header-toolbar__block-tools-toggle.is-primary.has-icon {
+ height: $button-size;
+ min-width: $button-size;
+ background-color: #fff;
+ color: #000;
+ margin-left: $grid-unit-10;
+ border-left: 1px solid #ddd;
+ padding-left: $grid-unit-15;
+
+ &:hover {
+ color: var(--wp-components-color-accent, var(--wp-admin-theme-color, #007cba));
+ }
+ }
+}
+
+.edit-post-header-block-toolbar {
+ position: absolute;
+ left: $grid-unit-70 + $grid-unit-05;
+ display: inline-flex;
+ flex-grow: 1;
+ align-items: center;
+ border: none;
+ padding-left: 24px; //compensates for the edit-post-header-document-toolbar__left left padding
+ // height: 32px;
+ // overflow: hidden; //limits the height of the separators
+
+ > .edit-post-header-toolbar__inserter-toggle {
+ display: inline-flex;
+
+ svg {
+ transition: transform cubic-bezier(0.165, 0.84, 0.44, 1) 0.2s;
+ @include reduce-motion("transition");
+ }
+
+ &.is-pressed {
+ svg {
+ transform: rotate(45deg);
+ }
+ }
+ }
+
+ & > button.components-button.edit-post-header-toolbar__document-tools-toggle.is-primary.has-icon {
height: $button-size;
min-width: $button-size;
padding: 6px;
+ background-color: #fff;
+ color: #000;
+ border-right: 1px solid #ddd;
+
+ &:hover {
+ color: var(--wp-components-color-accent, var(--wp-admin-theme-color, #007cba));
+ }
+ }
+
+ // The Toolbar component adds different styles to buttons, so we reset them
+ // here to the original button styles
+ & > button.components-button.edit-post-header-toolbar__inserter-toggle.is-primary.has-icon {
+ height: 32px;
+ margin-right: 8px;
+ min-width: 32px;
+ padding: 0;
+ width: 32px;
&.is-pressed {
background: $gray-900;
@@ -57,28 +140,35 @@
&::before {
display: none;
}
+
+ .show-icon-labels & {
+ width: auto;
+ height: 36px;
+ padding: 0 $grid-unit-10;
+ }
}
+
}
// Reduced UI.
.edit-post-header.has-reduced-ui {
@include break-small () {
// Apply transition to every button but the first one.
- .edit-post-header-toolbar__left > * + .components-button,
- .edit-post-header-toolbar__left > * + .components-dropdown > [aria-expanded="false"] {
+ .edit-post-header-document-toolbar__left > * + .components-button,
+ .edit-post-header-document-toolbar__left > * + .components-dropdown > [aria-expanded="false"] {
transition: opacity 0.1s linear;
@include reduce-motion("transition");
}
// Zero out opacity unless hovered.
- &:not(:hover) .edit-post-header-toolbar__left > * + .components-button,
- &:not(:hover) .edit-post-header-toolbar__left > * + .components-dropdown > [aria-expanded="false"] {
+ &:not(:hover) .edit-post-header-document-toolbar__left > * + .components-button,
+ &:not(:hover) .edit-post-header-document-toolbar__left > * + .components-dropdown > [aria-expanded="false"] {
opacity: 0;
}
}
}
-.edit-post-header-toolbar__left {
+.edit-post-header-document-toolbar__left {
display: inline-flex;
align-items: center;
padding-left: $grid-unit-10;
@@ -92,7 +182,7 @@
}
}
-.edit-post-header-toolbar .edit-post-header-toolbar__left > .edit-post-header-toolbar__inserter-toggle.has-icon {
+.edit-post-header-document-toolbar .edit-post-header-document-toolbar__left > .edit-post-header-document-toolbar__inserter-toggle.has-icon {
margin-right: $grid-unit-10;
// Special dimensions for this button.
min-width: 32px;
@@ -107,6 +197,6 @@
}
}
-.show-icon-labels .edit-post-header-toolbar__left > * + * {
+.show-icon-labels .edit-post-header-document-toolbar__left > * + * {
margin-left: $grid-unit-10;
}
diff --git a/packages/edit-post/src/components/header/index.js b/packages/edit-post/src/components/header/index.js
index a7bdccbfe44d9..9a6f3791a8161 100644
--- a/packages/edit-post/src/components/header/index.js
+++ b/packages/edit-post/src/components/header/index.js
@@ -1,11 +1,25 @@
/**
* WordPress dependencies
*/
+import { __ } from '@wordpress/i18n';
import { PostSavedState, PostPreviewButton } from '@wordpress/editor';
+import { useEffect, useState, useRef } from '@wordpress/element';
import { useSelect } from '@wordpress/data';
import { PinnedItems } from '@wordpress/interface';
import { useViewportMatch } from '@wordpress/compose';
-import { __unstableMotion as motion } from '@wordpress/components';
+import {
+ __unstableMotion as motion,
+ Button,
+ ToolbarItem,
+} from '@wordpress/components';
+import {
+ BlockIcon,
+ NavigableToolbar,
+ store as blockEditorStore,
+ BlockToolbar,
+ useBlockDisplayInformation,
+} from '@wordpress/block-editor';
+import { levelUp } from '@wordpress/icons';
/**
* Internal dependencies
@@ -18,17 +32,68 @@ import { default as DevicePreview } from '../device-preview';
import MainDashboardButton from './main-dashboard-button';
import { store as editPostStore } from '../../store';
import TemplateTitle from './template-title';
+import InserterButton from './inserter-button';
+
+function MaybeHide( { children, isHidden, onFocus } ) {
+ if ( isHidden ) {
+ return (
+
+ { children }
+
+ );
+ }
+ return children;
+}
+
+function ShowDocumentToolbarButton( { onClick } ) {
+ return (
+
+ );
+}
+
+function ShowBlockToolbarButton( { onClick, icon } ) {
+ return (
+
}
+ onClick={ onClick }
+ />
+ );
+}
function Header( { setEntitiesSavedStatesCallback } ) {
const isLargeViewport = useViewportMatch( 'large' );
+ const isMobileViewPort = useViewportMatch( 'medium', '<' );
+ const isDesktopViewport = useViewportMatch( 'medium', '>=' );
const {
hasActiveMetaboxes,
isPublishSidebarOpened,
isSaving,
+ hasFixedToolbar,
+ hasSelectedBlocks,
showIconLabels,
isDistractionFreeMode,
- } = useSelect(
- ( select ) => ( {
+ isNavigationMode,
+ selectedBlockClientId,
+ } = useSelect( ( select ) => {
+ const {
+ getSettings,
+ getSelectedBlockClientIds,
+ getSelectedBlockClientId,
+ isNavigationMode: _isNavigationMode,
+ } = select( blockEditorStore );
+ const settings = getSettings();
+ const _selectedBlockClientIds = getSelectedBlockClientIds();
+ return {
+ hasSelectedBlocks: !! _selectedBlockClientIds.length,
hasActiveMetaboxes: select( editPostStore ).hasMetaBoxes(),
isPublishSidebarOpened:
select( editPostStore ).isPublishSidebarOpened(),
@@ -37,12 +102,46 @@ function Header( { setEntitiesSavedStatesCallback } ) {
select( editPostStore ).isFeatureActive( 'showIconLabels' ),
isDistractionFreeMode:
select( editPostStore ).isFeatureActive( 'distractionFree' ),
- } ),
- []
- );
+ hasFixedToolbar: settings.hasFixedToolbar,
+ isNavigationMode: _isNavigationMode(),
+ selectedBlockClientId: getSelectedBlockClientId(),
+ };
+ } );
const isDistractionFree = isDistractionFreeMode && isLargeViewport;
+ const [ headerToolbar, setHeaderToolbar ] = useState( 'document' );
+
+ const documentToolsInserterButton = useRef();
+ const blockToolsInserterButton = useRef();
+
+ const blockInformation = useBlockDisplayInformation(
+ selectedBlockClientId
+ );
+
+ useEffect( () => {
+ if (
+ headerToolbar === 'document' &&
+ documentToolsInserterButton.current
+ ) {
+ documentToolsInserterButton.current.focus();
+ }
+
+ if ( headerToolbar === 'block' && blockToolsInserterButton.current ) {
+ blockToolsInserterButton.current.focus();
+ }
+ }, [ headerToolbar ] );
+
+ useEffect( () => {
+ if ( isNavigationMode ) {
+ setHeaderToolbar( 'document' );
+ } else if ( hasSelectedBlocks ) {
+ setHeaderToolbar( 'block' );
+ } else {
+ setHeaderToolbar( 'document' );
+ }
+ }, [ hasSelectedBlocks, isNavigationMode ] );
+
const slideY = {
hidden: isDistractionFree ? { y: '-50' } : { y: 0 },
hover: { y: 0, transition: { type: 'tween', delay: 0.2 } },
@@ -53,6 +152,8 @@ function Header( { setEntitiesSavedStatesCallback } ) {
hover: { x: 0, transition: { type: 'tween', delay: 0.2 } },
};
+ const blockToolbarAriaLabel = __( 'Block tools' );
+
return (
@@ -68,7 +169,30 @@ function Header( { setEntitiesSavedStatesCallback } ) {
transition={ { type: 'tween', delay: 0.8 } }
className="edit-post-header__toolbar"
>
-
+ { ( ! hasFixedToolbar ||
+ isNavigationMode ||
+ isMobileViewPort ) && }
+ { hasFixedToolbar &&
+ ! isNavigationMode &&
+ isDesktopViewport && (
+ setHeaderToolbar( 'document' ) }
+ >
+ (
+
+ setHeaderToolbar( 'block' )
+ }
+ icon={ blockInformation.icon }
+ />
+ ) }
+ />
+
+ ) }
) }
+ setHeaderToolbar( 'block' ) }
+ >
+
+
+ setHeaderToolbar( 'document' ) }
+ />
+
+
+
);
}
diff --git a/packages/edit-post/src/components/header/inserter-button/index.js b/packages/edit-post/src/components/header/inserter-button/index.js
new file mode 100644
index 0000000000000..8f21de3145c65
--- /dev/null
+++ b/packages/edit-post/src/components/header/inserter-button/index.js
@@ -0,0 +1,83 @@
+/**
+ * WordPress dependencies
+ */
+import { useCallback, forwardRef } from '@wordpress/element';
+import { __, _x } from '@wordpress/i18n';
+import { Button, ToolbarItem } from '@wordpress/components';
+import { plus } from '@wordpress/icons';
+import { useSelect, useDispatch } from '@wordpress/data';
+import { store as blockEditorStore } from '@wordpress/block-editor';
+import { store as editorStore } from '@wordpress/editor';
+/**
+ * Internal dependencies
+ */
+import { store as editPostStore } from '../../../store';
+
+const preventDefault = ( event ) => {
+ event.preventDefault();
+};
+
+function InserterButton( {}, inserterButton ) {
+ const { setIsInserterOpened } = useDispatch( editPostStore );
+ const { isInserterEnabled, isInserterOpened, showIconLabels } = useSelect(
+ ( select ) => {
+ const {
+ hasInserterItems,
+ getBlockRootClientId,
+ getBlockSelectionEnd,
+ } = select( blockEditorStore );
+ const { getEditorSettings } = select( editorStore );
+ const { getEditorMode, isFeatureActive } = select( editPostStore );
+
+ return {
+ // This setting (richEditingEnabled) should not live in the block editor's setting.
+ isInserterEnabled:
+ getEditorMode() === 'visual' &&
+ getEditorSettings().richEditingEnabled &&
+ hasInserterItems(
+ getBlockRootClientId( getBlockSelectionEnd() )
+ ),
+ isInserterOpened: select( editPostStore ).isInserterOpened(),
+ showIconLabels: isFeatureActive( 'showIconLabels' ),
+ };
+ },
+ []
+ );
+
+ const toggleInserter = useCallback( () => {
+ if ( isInserterOpened ) {
+ // Focusing the inserter button should close the inserter popover.
+ // However, there are some cases it won't close when the focus is lost.
+ // See https://github.com/WordPress/gutenberg/issues/43090 for more details.
+ inserterButton.current.focus();
+ setIsInserterOpened( false );
+ } else {
+ setIsInserterOpened( true );
+ }
+ }, [ isInserterOpened, setIsInserterOpened ] );
+
+ /* translators: button label text should, if possible, be under 16 characters. */
+ const longLabel = _x(
+ 'Toggle block inserter',
+ 'Generic label for block inserter button'
+ );
+ const shortLabel = ! isInserterOpened ? __( 'Add' ) : __( 'Close' );
+
+ return (
+
+ );
+}
+
+export default forwardRef( InserterButton );
diff --git a/packages/edit-post/src/components/header/style.scss b/packages/edit-post/src/components/header/style.scss
index 160543684a702..545d57edb0f3c 100644
--- a/packages/edit-post/src/components/header/style.scss
+++ b/packages/edit-post/src/components/header/style.scss
@@ -238,3 +238,23 @@
z-index: 35;
}
}
+
+.maybeHide {
+ position: fixed;
+ top: -9999px;
+}
+
+.maybeHide:focus-within {
+ position: relative;
+ top: 0;
+}
+
+.maybeHide:focus-within > .edit-post-header-document-toolbar .edit-post-header-document-toolbar__left > .components-button.edit-post-header-toolbar__block-tools-toggle,
+.maybeHide:focus-within + .edit-post-header-block-toolbar > .edit-post-header-toolbar__document-tools-toggle,
+.maybeHide:focus-within + .edit-post-header-block-toolbar > .edit-post-header-toolbar__inserter-toggle {
+ display: none;
+}
+
+// .maybeHide:focus-within + .edit-post-header-block-toolbar {
+// padding-left: 0;
+// }
diff --git a/packages/edit-post/src/components/header/writing-menu/index.js b/packages/edit-post/src/components/header/writing-menu/index.js
index 6cea56392381e..ac21030cc9cd6 100644
--- a/packages/edit-post/src/components/header/writing-menu/index.js
+++ b/packages/edit-post/src/components/header/writing-menu/index.js
@@ -6,10 +6,7 @@ import { MenuGroup } from '@wordpress/components';
import { __, _x } from '@wordpress/i18n';
import { useViewportMatch } from '@wordpress/compose';
import { displayShortcut } from '@wordpress/keycodes';
-import {
- PreferenceToggleMenuItem,
- store as preferencesStore,
-} from '@wordpress/preferences';
+import { PreferenceToggleMenuItem } from '@wordpress/preferences';
import { store as blockEditorStore } from '@wordpress/block-editor';
/**
@@ -32,13 +29,11 @@ function WritingMenu() {
const { setIsInserterOpened, setIsListViewOpened, closeGeneralSidebar } =
useDispatch( postEditorStore );
- const { set: setPreference } = useDispatch( preferencesStore );
const { selectBlock } = useDispatch( blockEditorStore );
const toggleDistractionFree = () => {
registry.batch( () => {
- setPreference( 'core/edit-post', 'fixedToolbar', false );
setIsInserterOpened( false );
setIsListViewOpened( false );
closeGeneralSidebar();
diff --git a/packages/edit-post/src/components/keyboard-shortcuts/index.js b/packages/edit-post/src/components/keyboard-shortcuts/index.js
index 13a989de7fd07..c330c7aac250d 100644
--- a/packages/edit-post/src/components/keyboard-shortcuts/index.js
+++ b/packages/edit-post/src/components/keyboard-shortcuts/index.js
@@ -11,7 +11,6 @@ import { __ } from '@wordpress/i18n';
import { store as editorStore } from '@wordpress/editor';
import { store as blockEditorStore } from '@wordpress/block-editor';
import { store as noticesStore } from '@wordpress/notices';
-import { store as preferencesStore } from '@wordpress/preferences';
import { createBlock } from '@wordpress/blocks';
/**
@@ -45,10 +44,7 @@ function KeyboardShortcuts() {
} = useDispatch( editPostStore );
const { registerShortcut } = useDispatch( keyboardShortcutsStore );
- const { set: setPreference } = useDispatch( preferencesStore );
-
const toggleDistractionFree = () => {
- setPreference( 'core/edit-post', 'fixedToolbar', false );
setIsInserterOpened( false );
setIsListViewOpened( false );
closeGeneralSidebar();
diff --git a/packages/edit-post/src/components/preferences-modal/index.js b/packages/edit-post/src/components/preferences-modal/index.js
index 77c6383b13f32..41aaa12356042 100644
--- a/packages/edit-post/src/components/preferences-modal/index.js
+++ b/packages/edit-post/src/components/preferences-modal/index.js
@@ -19,7 +19,6 @@ import {
PreferencesModalTabs,
PreferencesModalSection,
} from '@wordpress/interface';
-import { store as preferencesStore } from '@wordpress/preferences';
/**
* Internal dependencies
@@ -65,10 +64,7 @@ export default function EditPostPreferencesModal() {
const { closeGeneralSidebar, setIsListViewOpened, setIsInserterOpened } =
useDispatch( editPostStore );
- const { set: setPreference } = useDispatch( preferencesStore );
-
const toggleDistractionFree = () => {
- setPreference( 'core/edit-post', 'fixedToolbar', false );
setIsInserterOpened( false );
setIsListViewOpened( false );
closeGeneralSidebar();
diff --git a/packages/icons/src/index.js b/packages/icons/src/index.js
index 45ead66387db7..7c4be01f3fb95 100644
--- a/packages/icons/src/index.js
+++ b/packages/icons/src/index.js
@@ -119,6 +119,7 @@ export { default as key } from './library/key';
export { default as keyboardClose } from './library/keyboard-close';
export { default as keyboardReturn } from './library/keyboard-return';
export { default as layout } from './library/layout';
+export { default as levelUp } from './library/level-up';
export { default as lifesaver } from './library/lifesaver';
export { default as lineDashed } from './library/line-dashed';
export { default as lineDotted } from './library/line-dotted';
diff --git a/packages/icons/src/library/level-up.js b/packages/icons/src/library/level-up.js
new file mode 100644
index 0000000000000..fc992c8dbada5
--- /dev/null
+++ b/packages/icons/src/library/level-up.js
@@ -0,0 +1,12 @@
+/**
+ * WordPress dependencies
+ */
+import { SVG, Path } from '@wordpress/primitives';
+
+const levelUp = (
+
+);
+
+export default levelUp;