From b45f22c3e2d33367a43122e6e13ce5ac41cf1ebf Mon Sep 17 00:00:00 2001 From: Jerry Jones Date: Thu, 7 Dec 2023 16:16:08 -0600 Subject: [PATCH] Refactor : Add variant prop and wrapper (#56335) - Combine `` into ``. This brings all the necessary functionality for `` to be used, mainly by wrapping it in a ``. - Remove ``. This is no longer needed, and was originally intended for Contextual to mean Popover. - Replace usage of `` in edit-site, edit-post, edit-widget, etc headers with `` - Refactor `` to become `` to better reflect its purpose. - Add `` with new props of `__experimentalInitialIndex`, `__experimentalOnIndexChange`, and `focusOnMount`. - Export `` from `` with locked `__experimentalInitialIndex`, `__experimentalOnIndexChange`, and `focusOnMount` as `undefined`. Public API props are `hideDragHandles` and `variant` with a default of `unstyled`. - Remove concept of `isFixed` from the ``. The styles of the current small-screen fixed toolbar will become the default. The popover and top toolbars will have its own CSS overrides applied. - Split `` and `` into separate components for simplicity. - Top Toolbar now means. You have to implement the `` on your own. --- packages/block-editor/README.md | 8 + .../block-parent-selector/style.scss | 11 - .../src/components/block-toolbar/index.js | 275 ++++++++++++------ .../src/components/block-toolbar/style.scss | 116 ++++---- .../src/components/block-tools/back-compat.js | 4 +- .../block-tools/block-contextual-toolbar.js | 100 ------- .../block-tools/block-toolbar-breadcrumb.js | 46 +++ .../block-tools/block-toolbar-popover.js | 90 ++++++ .../src/components/block-tools/index.js | 71 +++-- .../block-tools/selected-block-tools.js | 127 -------- .../src/components/block-tools/style.scss | 232 ++++----------- .../components/navigable-toolbar/README.md | 2 + .../src/components/navigable-toolbar/index.js | 4 +- packages/block-editor/src/private-apis.js | 2 - packages/block-editor/src/style.scss | 1 - .../src/components/header/index.js | 27 +- .../src/components/header/style.scss | 2 +- .../components/sidebar-block-editor/index.js | 14 +- .../sidebar-block-editor/style.scss | 20 -- .../edit-post/src/components/header/index.js | 7 +- .../src/components/header/style.scss | 55 +++- .../edit-post/src/components/layout/index.js | 18 +- packages/edit-post/src/editor.js | 9 +- .../src/components/block-editor/style.scss | 11 - .../block-editor/use-site-editor-settings.js | 80 ++--- .../edit-site/src/components/editor/index.js | 8 +- .../src/components/header-edit-mode/index.js | 6 +- .../components/header-edit-mode/style.scss | 41 ++- .../src/components/header/index.js | 7 +- .../src/components/header/style.scss | 27 +- .../index.js | 4 + .../index.js | 5 +- storybook/stories/playground/box/index.js | 2 + .../playground/with-undo-redo/index.js | 2 + .../playground/with-undo-redo/style.css | 6 +- .../specs/editor/various/is-typing.spec.js | 40 ++- .../various/shortcut-focus-toolbar.spec.js | 4 + 37 files changed, 725 insertions(+), 759 deletions(-) delete mode 100644 packages/block-editor/src/components/block-parent-selector/style.scss delete mode 100644 packages/block-editor/src/components/block-tools/block-contextual-toolbar.js create mode 100644 packages/block-editor/src/components/block-tools/block-toolbar-breadcrumb.js create mode 100644 packages/block-editor/src/components/block-tools/block-toolbar-popover.js delete mode 100644 packages/block-editor/src/components/block-tools/selected-block-tools.js diff --git a/packages/block-editor/README.md b/packages/block-editor/README.md index 2d6a5627a52a44..56ab5f1bd94d93 100644 --- a/packages/block-editor/README.md +++ b/packages/block-editor/README.md @@ -280,10 +280,18 @@ _Returns_ ### BlockToolbar +Renders the block toolbar. + _Related_ - +_Parameters_ + +- _props_ `Object`: Components props. +- _props.hideDragHandle_ `boolean`: Show or hide the Drag Handle for drag and drop functionality. +- _props.variant_ `string`: Style variant of the toolbar, also passed to the Dropdowns rendered from Block Toolbar Buttons. + ### BlockTools Renders block tools (the block toolbar, select/navigation mode toolbar, the insertion point and a slot for the inline rich text toolbar). Must be wrapped around the block content and editor styles wrapper or iframe. diff --git a/packages/block-editor/src/components/block-parent-selector/style.scss b/packages/block-editor/src/components/block-parent-selector/style.scss deleted file mode 100644 index c5a1869835188c..00000000000000 --- a/packages/block-editor/src/components/block-parent-selector/style.scss +++ /dev/null @@ -1,11 +0,0 @@ -.block-editor-block-parent-selector { - background: $white; - border-radius: $radius-block-ui; - - .block-editor-block-parent-selector__button { - width: $grid-unit-60; - height: $grid-unit-60; - border: $border-width solid $gray-900; - border-radius: $radius-block-ui; - } -} diff --git a/packages/block-editor/src/components/block-toolbar/index.js b/packages/block-editor/src/components/block-toolbar/index.js index 963cd8a475328a..7bb52a7e8f0906 100644 --- a/packages/block-editor/src/components/block-toolbar/index.js +++ b/packages/block-editor/src/components/block-toolbar/index.js @@ -6,6 +6,7 @@ import classnames from 'classnames'; /** * WordPress dependencies */ +import { __ } from '@wordpress/i18n'; import { useSelect } from '@wordpress/data'; import { useRef } from '@wordpress/element'; import { useViewportMatch } from '@wordpress/compose'; @@ -32,38 +33,82 @@ import BlockEditVisuallyButton from '../block-edit-visually-button'; import { useShowHoveredOrFocusedGestures } from './utils'; import { store as blockEditorStore } from '../../store'; import __unstableBlockNameContext from './block-name-context'; +import NavigableToolbar from '../navigable-toolbar'; +import { useHasAnyBlockControls } from '../block-controls/use-has-block-controls'; -const BlockToolbar = ( { hideDragHandle } ) => { - const { blockClientIds, blockType, isValid, isVisual, blockEditingMode } = - useSelect( ( select ) => { - const { - getBlockName, - getBlockMode, - getSelectedBlockClientIds, - isBlockValid, - getBlockRootClientId, - getBlockEditingMode, - } = select( blockEditorStore ); - const selectedBlockClientIds = getSelectedBlockClientIds(); - const selectedBlockClientId = selectedBlockClientIds[ 0 ]; - const blockRootClientId = getBlockRootClientId( - selectedBlockClientId - ); - return { - blockClientIds: selectedBlockClientIds, - blockType: - selectedBlockClientId && - getBlockType( getBlockName( selectedBlockClientId ) ), - rootClientId: blockRootClientId, - isValid: selectedBlockClientIds.every( ( id ) => - isBlockValid( id ) - ), - isVisual: selectedBlockClientIds.every( - ( id ) => getBlockMode( id ) === 'visual' - ), - blockEditingMode: getBlockEditingMode( selectedBlockClientId ), - }; - }, [] ); +/** + * Renders the block toolbar. + * + * @see https://github.com/WordPress/gutenberg/blob/HEAD/packages/block-editor/src/components/block-toolbar/README.md + * + * @param {Object} props Components props. + * @param {boolean} props.hideDragHandle Show or hide the Drag Handle for drag and drop functionality. + * @param {boolean} props.focusOnMount Focus the toolbar when mounted. + * @param {number} props.__experimentalInitialIndex The initial index of the toolbar item to focus. + * @param {Function} props.__experimentalOnIndexChange Callback function to be called when the index of the focused toolbar item changes. + * @param {string} props.variant Style variant of the toolbar, also passed to the Dropdowns rendered from Block Toolbar Buttons. + */ +export function PrivateBlockToolbar( { + hideDragHandle, + focusOnMount, + __experimentalInitialIndex, + __experimentalOnIndexChange, + variant = 'unstyled', +} ) { + const { + blockClientId, + blockClientIds, + isDefaultEditingMode, + blockType, + shouldShowVisualToolbar, + showParentSelector, + } = useSelect( ( select ) => { + const { + getBlockName, + getBlockMode, + getBlockParents, + getSelectedBlockClientIds, + isBlockValid, + getBlockRootClientId, + getBlockEditingMode, + } = select( blockEditorStore ); + const selectedBlockClientIds = getSelectedBlockClientIds(); + const selectedBlockClientId = selectedBlockClientIds[ 0 ]; + const blockRootClientId = getBlockRootClientId( selectedBlockClientId ); + const parents = getBlockParents( selectedBlockClientId ); + const firstParentClientId = parents[ parents.length - 1 ]; + const parentBlockName = getBlockName( firstParentClientId ); + const parentBlockType = getBlockType( parentBlockName ); + const _isDefaultEditingMode = + getBlockEditingMode( selectedBlockClientId ) === 'default'; + const isValid = selectedBlockClientIds.every( ( id ) => + isBlockValid( id ) + ); + const isVisual = selectedBlockClientIds.every( + ( id ) => getBlockMode( id ) === 'visual' + ); + return { + blockClientId: selectedBlockClientId, + blockClientIds: selectedBlockClientIds, + isDefaultEditingMode: _isDefaultEditingMode, + blockType: + selectedBlockClientId && + getBlockType( getBlockName( selectedBlockClientId ) ), + + shouldShowVisualToolbar: isValid && isVisual, + rootClientId: blockRootClientId, + showParentSelector: + parentBlockType && + getBlockEditingMode( firstParentClientId ) === 'default' && + hasBlockSupport( + parentBlockType, + '__experimentalParentSelector', + true + ) && + selectedBlockClientIds.length === 1 && + _isDefaultEditingMode, + }; + }, [] ); const toolbarWrapperRef = useRef( null ); @@ -76,86 +121,126 @@ const BlockToolbar = ( { hideDragHandle } ) => { const isLargeViewport = ! useViewportMatch( 'medium', '<' ); - if ( blockType ) { - if ( ! hasBlockSupport( blockType, '__experimentalToolbar', true ) ) { - return null; - } - } + const isToolbarEnabled = + blockType && + hasBlockSupport( blockType, '__experimentalToolbar', true ); + const hasAnyBlockControls = useHasAnyBlockControls(); - if ( blockClientIds.length === 0 ) { + if ( + ! isToolbarEnabled || + ( ! isDefaultEditingMode && ! hasAnyBlockControls ) + ) { return null; } - const shouldShowVisualToolbar = isValid && isVisual; const isMultiToolbar = blockClientIds.length > 1; const isSynced = isReusableBlock( blockType ) || isTemplatePart( blockType ); - const classes = classnames( 'block-editor-block-toolbar', { + // Shifts the toolbar to make room for the parent block selector. + const classes = classnames( 'block-editor-block-contextual-toolbar', { + 'has-parent': showParentSelector, + } ); + + const innerClasses = classnames( 'block-editor-block-toolbar', { 'is-synced': isSynced, } ); return ( -
- { ! isMultiToolbar && - isLargeViewport && - blockEditingMode === 'default' && } - { ( shouldShowVisualToolbar || isMultiToolbar ) && - blockEditingMode === 'default' && ( -
- - - { ! isMultiToolbar && ( - +
+ { ! isMultiToolbar && + isLargeViewport && + isDefaultEditingMode && } + { ( shouldShowVisualToolbar || isMultiToolbar ) && + isDefaultEditingMode && ( +
+ + + { ! isMultiToolbar && ( + + ) } + - ) } - - -
+ +
+ ) } + { shouldShowVisualToolbar && isMultiToolbar && ( + + ) } + { shouldShowVisualToolbar && ( + <> + + + + + + <__unstableBlockNameContext.Provider + value={ blockType?.name } + > + <__unstableBlockToolbarLastItem.Slot /> + + + ) } + + { isDefaultEditingMode && ( + ) } - { shouldShowVisualToolbar && isMultiToolbar && ( - - ) } - { shouldShowVisualToolbar && ( - <> - - - - - - <__unstableBlockNameContext.Provider - value={ blockType?.name } - > - <__unstableBlockToolbarLastItem.Slot /> - - - ) } - - { blockEditingMode === 'default' && ( - - ) } -
+
+
); -}; +} /** + * Renders the block toolbar. + * * @see https://github.com/WordPress/gutenberg/blob/HEAD/packages/block-editor/src/components/block-toolbar/README.md + * + * @param {Object} props Components props. + * @param {boolean} props.hideDragHandle Show or hide the Drag Handle for drag and drop functionality. + * @param {string} props.variant Style variant of the toolbar, also passed to the Dropdowns rendered from Block Toolbar Buttons. */ -export default BlockToolbar; +export default function BlockToolbar( { hideDragHandle, variant } ) { + return ( + + ); +} diff --git a/packages/block-editor/src/components/block-toolbar/style.scss b/packages/block-editor/src/components/block-toolbar/style.scss index 3f8a7057aef84f..85020cea2aa23f 100644 --- a/packages/block-editor/src/components/block-toolbar/style.scss +++ b/packages/block-editor/src/components/block-toolbar/style.scss @@ -56,57 +56,66 @@ } } -.block-editor-block-contextual-toolbar.is-fixed { +.block-editor-block-contextual-toolbar { position: sticky; top: 0; z-index: z-index(".block-editor-block-popover"); display: block; width: 100%; -} + // Block UI appearance. + background-color: $white; + flex-shrink: 3; + + // Raise the specificity. + &.components-accessible-toolbar { + border: none; + border-bottom: $border-width solid $gray-200; + border-radius: 0; + } -// on desktop browsers the fixed toolbar has tweaked borders -@include break-medium() { - .block-editor-block-contextual-toolbar.is-fixed { - .block-editor-block-toolbar { - .components-toolbar-group, - .components-toolbar { - border-right: none; - - &::after { - content: ""; - width: $border-width; - margin-top: $grid-unit + $grid-unit-05; - margin-bottom: $grid-unit + $grid-unit-05; - background-color: $gray-300; - margin-left: $grid-unit; - } - - & .components-toolbar-group.components-toolbar-group { - &::after { - display: none; - } - } - } + .block-editor-block-toolbar { + overflow: auto; + overflow-y: hidden; - > :last-child, - > :last-child .components-toolbar-group, - > :last-child .components-toolbar { - &::after { - display: none; - } + > :last-child, + > :last-child .components-toolbar-group, + > :last-child .components-toolbar { + &::after { + display: none; } } } -} -.block-editor-block-contextual-toolbar.has-parent:not(.is-fixed) { - margin-left: calc(#{$grid-unit-60} + #{$grid-unit-10}); + .block-editor-block-toolbar .components-toolbar-group, + .block-editor-block-toolbar .components-toolbar { + border-right-color: $gray-200; + } - .show-icon-labels & { - margin-left: 0; + & > .block-editor-block-toolbar { + flex-grow: initial; + width: initial; + } + + .block-editor-block-parent-selector { + position: relative; + + // Parent selector dot divider + &::after { + content: "\00B7"; + position: absolute; + font-size: 16px; + right: 0; + bottom: $grid-unit-20; + } + } + + .block-editor-block-parent-selector__button { + position: relative; + top: -1px; } } + // Block controls. .block-editor-block-toolbar__block-controls { // Switcher. @@ -165,7 +174,6 @@ } // Padding overrides. - .components-accessible-toolbar .components-toolbar-group > div:first-child:last-child > .components-button.has-icon { padding-left: 6px; padding-right: 6px; @@ -181,10 +189,12 @@ } // Parent selector overrides - - .block-editor-block-parent-selector__button { + .block-editor-block-parent-selector .block-editor-block-parent-selector__button { border-top-right-radius: 0; border-bottom-right-radius: 0; + padding-left: $grid-unit-15; + padding-right: $grid-unit-15; + text-wrap: nowrap; .block-editor-block-icon { width: 0; @@ -210,25 +220,17 @@ // Mover overrides. .block-editor-block-toolbar__block-controls .block-editor-block-mover { - border-left: 1px solid $gray-900; + border-left: 1px solid $gray-300; margin-left: 6px; margin-right: -6px; white-space: nowrap; } - .block-editor-block-contextual-toolbar.is-fixed .block-editor-block-toolbar__block-controls .block-editor-block-mover { - border-left-color: $gray-200; - } - .block-editor-block-mover .block-editor-block-mover__drag-handle.has-icon { padding-left: $grid-unit-15; padding-right: $grid-unit-15; } - .block-editor-block-contextual-toolbar.is-fixed .block-editor-block-mover__move-button-container { - border-width: 0; - } - @include break-small() { // Specificity override for https://github.com/WordPress/gutenberg/blob/try/block-toolbar-labels/packages/block-editor/src/components/block-mover/style.scss#L69 .is-up-button.is-up-button.is-up-button { @@ -237,17 +239,9 @@ order: 1; } - .block-editor-block-mover__move-button-container { - border-left: 1px solid $gray-900; - } - .is-down-button.is-down-button.is-down-button { order: 2; } - - .block-editor-block-contextual-toolbar.is-fixed .block-editor-block-mover__move-button-container::before { - background: $gray-300; - } } .block-editor-block-contextual-toolbar .block-editor-block-mover.is-horizontal .block-editor-block-mover-button.block-editor-block-mover-button { @@ -260,16 +254,6 @@ flex-shrink: 1; } - @include break-medium() { - .block-editor-block-contextual-toolbar.is-fixed { - .components-toolbar, - .components-toolbar-group { - flex-shrink: 0; - } - } - } - - .block-editor-rich-text__inline-format-toolbar-group { .components-button + .components-button { margin-left: 6px; diff --git a/packages/block-editor/src/components/block-tools/back-compat.js b/packages/block-editor/src/components/block-tools/back-compat.js index 029419926e9ed5..14027760be1e6a 100644 --- a/packages/block-editor/src/components/block-tools/back-compat.js +++ b/packages/block-editor/src/components/block-tools/back-compat.js @@ -9,7 +9,7 @@ import deprecated from '@wordpress/deprecated'; * Internal dependencies */ import InsertionPoint, { InsertionPointOpenRef } from './insertion-point'; -import BlockPopover from './selected-block-tools'; +import BlockToolbarPopover from './block-toolbar-popover'; export default function BlockToolsBackCompat( { children } ) { const openRef = useContext( InsertionPointOpenRef ); @@ -28,7 +28,7 @@ export default function BlockToolsBackCompat( { children } ) { return ( - + { children } ); diff --git a/packages/block-editor/src/components/block-tools/block-contextual-toolbar.js b/packages/block-editor/src/components/block-tools/block-contextual-toolbar.js deleted file mode 100644 index b24a25ee60ed42..00000000000000 --- a/packages/block-editor/src/components/block-tools/block-contextual-toolbar.js +++ /dev/null @@ -1,100 +0,0 @@ -/** - * External dependencies - */ -import classnames from 'classnames'; - -/** - * WordPress dependencies - */ -import { __ } from '@wordpress/i18n'; -import { hasBlockSupport, store as blocksStore } from '@wordpress/blocks'; -import { useSelect } from '@wordpress/data'; - -/** - * Internal dependencies - */ -import NavigableToolbar from '../navigable-toolbar'; -import BlockToolbar from '../block-toolbar'; -import { store as blockEditorStore } from '../../store'; -import { useHasAnyBlockControls } from '../block-controls/use-has-block-controls'; - -export default function BlockContextualToolbar( { - focusOnMount, - isFixed, - ...props -} ) { - const { - blockType, - blockEditingMode, - hasParents, - showParentSelector, - selectedBlockClientId, - } = useSelect( ( select ) => { - const { - getBlockName, - getBlockParents, - getSelectedBlockClientIds, - getBlockEditingMode, - } = select( blockEditorStore ); - const { getBlockType } = select( blocksStore ); - const selectedBlockClientIds = getSelectedBlockClientIds(); - const _selectedBlockClientId = selectedBlockClientIds[ 0 ]; - const parents = getBlockParents( _selectedBlockClientId ); - const firstParentClientId = parents[ parents.length - 1 ]; - const parentBlockName = getBlockName( firstParentClientId ); - const parentBlockType = getBlockType( parentBlockName ); - - return { - selectedBlockClientId: _selectedBlockClientId, - blockType: - _selectedBlockClientId && - getBlockType( getBlockName( _selectedBlockClientId ) ), - blockEditingMode: getBlockEditingMode( _selectedBlockClientId ), - hasParents: parents.length, - showParentSelector: - parentBlockType && - getBlockEditingMode( firstParentClientId ) === 'default' && - hasBlockSupport( - parentBlockType, - '__experimentalParentSelector', - true - ) && - selectedBlockClientIds.length <= 1 && - getBlockEditingMode( _selectedBlockClientId ) === 'default', - }; - }, [] ); - - const isToolbarEnabled = - blockType && - hasBlockSupport( blockType, '__experimentalToolbar', true ); - const hasAnyBlockControls = useHasAnyBlockControls(); - if ( - ! isToolbarEnabled || - ( blockEditingMode !== 'default' && ! hasAnyBlockControls ) - ) { - return null; - } - - // Shifts the toolbar to make room for the parent block selector. - const classes = classnames( 'block-editor-block-contextual-toolbar', { - 'has-parent': hasParents && showParentSelector, - 'is-fixed': isFixed, - } ); - - return ( - - - - ); -} diff --git a/packages/block-editor/src/components/block-tools/block-toolbar-breadcrumb.js b/packages/block-editor/src/components/block-tools/block-toolbar-breadcrumb.js new file mode 100644 index 00000000000000..77afb824101d41 --- /dev/null +++ b/packages/block-editor/src/components/block-tools/block-toolbar-breadcrumb.js @@ -0,0 +1,46 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; + +/** + * Internal dependencies + */ +import BlockSelectionButton from './block-selection-button'; +import BlockPopover from '../block-popover'; +import useBlockToolbarPopoverProps from './use-block-toolbar-popover-props'; +import useSelectedBlockToolProps from './use-selected-block-tool-props'; + +export default function BlockToolbarBreadcrumb( { + clientId, + __unstableContentRef, +} ) { + const { + capturingClientId, + isInsertionPointVisible, + lastClientId, + rootClientId, + } = useSelectedBlockToolProps( clientId ); + + const popoverProps = useBlockToolbarPopoverProps( { + contentElement: __unstableContentRef?.current, + clientId, + } ); + + return ( + + + + ); +} diff --git a/packages/block-editor/src/components/block-tools/block-toolbar-popover.js b/packages/block-editor/src/components/block-tools/block-toolbar-popover.js new file mode 100644 index 00000000000000..a50e5dc42b3712 --- /dev/null +++ b/packages/block-editor/src/components/block-tools/block-toolbar-popover.js @@ -0,0 +1,90 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; +/** + * WordPress dependencies + */ +import { useDispatch } from '@wordpress/data'; +import { useEffect, useRef } from '@wordpress/element'; +import { useShortcut } from '@wordpress/keyboard-shortcuts'; +/** + * Internal dependencies + */ +import BlockPopover from '../block-popover'; +import useBlockToolbarPopoverProps from './use-block-toolbar-popover-props'; +import useSelectedBlockToolProps from './use-selected-block-tool-props'; +import { store as blockEditorStore } from '../../store'; +import { PrivateBlockToolbar } from '../block-toolbar'; + +export default function BlockToolbarPopover( { + clientId, + isTyping, + __unstableContentRef, +} ) { + const { capturingClientId, isInsertionPointVisible, lastClientId } = + useSelectedBlockToolProps( clientId ); + + // Stores the active toolbar item index so the block toolbar can return focus + // to it when re-mounting. + const initialToolbarItemIndexRef = useRef(); + + useEffect( () => { + // Resets the index whenever the active block changes so this is not + // persisted. See https://github.com/WordPress/gutenberg/pull/25760#issuecomment-717906169 + initialToolbarItemIndexRef.current = undefined; + }, [ clientId ] ); + + const { stopTyping } = useDispatch( blockEditorStore ); + const isToolbarForced = useRef( false ); + + useShortcut( + 'core/block-editor/focus-toolbar', + () => { + isToolbarForced.current = true; + stopTyping( true ); + }, + { + isDisabled: false, + } + ); + + useEffect( () => { + isToolbarForced.current = false; + } ); + + const popoverProps = useBlockToolbarPopoverProps( { + contentElement: __unstableContentRef?.current, + clientId, + } ); + + return ( + ! isTyping && ( + + { + initialToolbarItemIndexRef.current = index; + } } + variant="toolbar" + /> + + ) + ); +} diff --git a/packages/block-editor/src/components/block-tools/index.js b/packages/block-editor/src/components/block-tools/index.js index bc2729fbb15990..969f36d878b875 100644 --- a/packages/block-editor/src/components/block-tools/index.js +++ b/packages/block-editor/src/components/block-tools/index.js @@ -2,7 +2,6 @@ * WordPress dependencies */ import { useSelect, useDispatch } from '@wordpress/data'; -import { useViewportMatch } from '@wordpress/compose'; import { Popover } from '@wordpress/components'; import { __unstableUseShortcutEventMatch as useShortcutEventMatch } from '@wordpress/keyboard-shortcuts'; import { useRef } from '@wordpress/element'; @@ -16,9 +15,9 @@ import { InsertionPointOpenRef, default as InsertionPoint, } from './insertion-point'; -import SelectedBlockTools from './selected-block-tools'; +import BlockToolbarPopover from './block-toolbar-popover'; +import BlockToolbarBreadcrumb from './block-toolbar-breadcrumb'; import { store as blockEditorStore } from '../../store'; -import BlockContextualToolbar from './block-contextual-toolbar'; import usePopoverScroll from '../block-popover/use-popover-scroll'; import ZoomOutModeInserters from './zoom-out-mode-inserters'; @@ -28,6 +27,7 @@ function selector( select ) { getFirstMultiSelectedBlockClientId, getBlock, getSettings, + hasMultiSelection, __unstableGetEditorMode, isTyping, } = select( blockEditorStore ); @@ -36,18 +36,35 @@ function selector( select ) { getSelectedBlockClientId() || getFirstMultiSelectedBlockClientId(); const { name = '', attributes = {} } = getBlock( clientId ) || {}; + const editorMode = __unstableGetEditorMode(); + const hasSelectedBlock = clientId && name; + const isEmptyDefaultBlock = isUnmodifiedDefaultBlock( { + name, + attributes, + } ); + const _showEmptyBlockSideInserter = + clientId && + ! isTyping() && + editorMode === 'edit' && + isUnmodifiedDefaultBlock( { name, attributes } ); + const maybeShowBreadcrumb = + hasSelectedBlock && + ! hasMultiSelection() && + ( editorMode === 'navigation' || editorMode === 'zoom-out' ); return { clientId, hasFixedToolbar: getSettings().hasFixedToolbar, - hasSelectedBlock: clientId && name, isTyping: isTyping(), - isZoomOutMode: __unstableGetEditorMode() === 'zoom-out', - showEmptyBlockSideInserter: - clientId && - ! isTyping() && - __unstableGetEditorMode() === 'edit' && - isUnmodifiedDefaultBlock( { name, attributes } ), + isZoomOutMode: editorMode === 'zoom-out', + showEmptyBlockSideInserter: _showEmptyBlockSideInserter, + showBreadcrumb: ! _showEmptyBlockSideInserter && maybeShowBreadcrumb, + showBlockToolbar: + ! getSettings().hasFixedToolbar && + ! _showEmptyBlockSideInserter && + hasSelectedBlock && + ! isEmptyDefaultBlock && + ! maybeShowBreadcrumb, }; } @@ -65,14 +82,14 @@ export default function BlockTools( { __unstableContentRef, ...props } ) { - const isLargeViewport = useViewportMatch( 'medium' ); const { clientId, hasFixedToolbar, - hasSelectedBlock, isTyping, isZoomOutMode, showEmptyBlockSideInserter, + showBreadcrumb, + showBlockToolbar, } = useSelect( selector, [] ); const isMatch = useShortcutEventMatch(); const { getSelectedBlockClientIds, getBlockRootClientId } = @@ -162,12 +179,6 @@ export default function BlockTools( { const blockToolbarRef = usePopoverScroll( __unstableContentRef ); const blockToolbarAfterRef = usePopoverScroll( __unstableContentRef ); - // Conditions for fixed toolbar - // 1. Not zoom out mode - // 2. It's a large viewport. If it's a smaller viewport, let the floating toolbar handle it as it already has styles attached to make it render that way. - // 3. Fixed toolbar is enabled - const isTopToolbar = ! isZoomOutMode && hasFixedToolbar && isLargeViewport; - return ( // eslint-disable-next-line jsx-a11y/no-static-element-interactions
@@ -177,11 +188,6 @@ export default function BlockTools( { __unstableContentRef={ __unstableContentRef } /> ) } - { /* If there is no slot available, such as in the standalone block editor, render within the editor */ } - - { ! isLargeViewport && ( // Small viewports always get a fixed toolbar - - ) } { showEmptyBlockSideInserter && ( ) } - { /* Even if the toolbar is fixed, the block popover is still - needed for navigation and zoom-out mode. */ } - { ! showEmptyBlockSideInserter && hasSelectedBlock && ( - + ) } + + { showBreadcrumb && ( + ) } - { /* Used for the inline rich text toolbar. */ } - { ! isTopToolbar && ( + { /* Used for the inline rich text toolbar. Until this toolbar is combined into BlockToolbar, someone implementing their own BlockToolbar will also need to use this to see the image caption toolbar. */ } + { ! isZoomOutMode && ! hasFixedToolbar && ( { - const { hasMultiSelection, __unstableGetEditorMode } = - select( blockEditorStore ); - - const editorMode = __unstableGetEditorMode(); - - return { - shouldShowBreadcrumb: - ! hasMultiSelection() && - ( editorMode === 'navigation' || editorMode === 'zoom-out' ), - }; - }, [] ); - - const isToolbarForced = useRef( false ); - const { shouldShowContextualToolbar, canFocusHiddenToolbar } = - useShouldContextualToolbarShow(); - - const { stopTyping } = useDispatch( blockEditorStore ); - - useShortcut( - 'core/block-editor/focus-toolbar', - () => { - isToolbarForced.current = true; - stopTyping( true ); - }, - { - isDisabled: ! canFocusHiddenToolbar, - } - ); - - useEffect( () => { - isToolbarForced.current = false; - } ); - - // Stores the active toolbar item index so the block toolbar can return focus - // to it when re-mounting. - const initialToolbarItemIndexRef = useRef(); - - useEffect( () => { - // Resets the index whenever the active block changes so this is not - // persisted. See https://github.com/WordPress/gutenberg/pull/25760#issuecomment-717906169 - initialToolbarItemIndexRef.current = undefined; - }, [ clientId ] ); - - const popoverProps = useBlockToolbarPopoverProps( { - contentElement: __unstableContentRef?.current, - clientId, - } ); - - if ( showEmptyBlockSideInserter ) { - return null; - } - - if ( shouldShowBreadcrumb || shouldShowContextualToolbar ) { - return ( - - { shouldShowContextualToolbar && ( - { - initialToolbarItemIndexRef.current = index; - } } - /> - ) } - { shouldShowBreadcrumb && ( - - ) } - - ); - } - - return null; -} diff --git a/packages/block-editor/src/components/block-tools/style.scss b/packages/block-editor/src/components/block-tools/style.scss index 07f22bb4946ea2..3371d795e6c033 100644 --- a/packages/block-editor/src/components/block-tools/style.scss +++ b/packages/block-editor/src/components/block-tools/style.scss @@ -85,178 +85,6 @@ } } -/** - * Block Toolbar when contextual. - */ - -.block-editor-block-contextual-toolbar { - // Block UI appearance. - display: inline-flex; - border: $border-width solid $gray-900; - border-radius: $radius-block-ui; - background-color: $white; - - .block-editor-block-toolbar .components-toolbar-group, - .block-editor-block-toolbar .components-toolbar { - border-right-color: $gray-900; - } - - &.is-fixed { - overflow: hidden; - - .block-editor-block-toolbar { - overflow: auto; - overflow-y: hidden; - } - - border-bottom: $border-width solid $gray-200; - border-radius: 0; - - .block-editor-block-toolbar .components-toolbar-group, - .block-editor-block-toolbar .components-toolbar { - border-right-color: $gray-200; - } - } - - @include break-medium() { - &.is-fixed { - & > .block-editor-block-toolbar { - flex-grow: initial; - width: initial; - - // Add a border as separator in the block toolbar. - &::before { - content: ""; - width: $border-width; - height: 3 * $grid-unit; - margin-top: $grid-unit + $grid-unit-05; - margin-right: 0; - background-color: $gray-300; - position: relative; - left: math.div(-$grid-unit-05, 2); - top: -1px; - } - } - - & > .block-editor-block-toolbar__group-collapse-fixed-toolbar { - border: none; - - .show-icon-labels & { - .components-button.has-icon { - // Hide the button icons when labels are set to display... - svg { - display: none; - } - // ... and display labels. - &::after { - content: attr(aria-label); - font-size: $helptext-font-size; - } - } - } - - // Add a border as separator in the block toolbar. - &::before { - content: ""; - width: $border-width; - height: 3 * $grid-unit; - margin-top: $grid-unit + $grid-unit-05; - margin-right: $grid-unit-10; - background-color: $gray-300; - position: relative; - left: 0; - top: -1px; - } - } - - & > .block-editor-block-toolbar__group-expand-fixed-toolbar { - border: none; - - .show-icon-labels & { - width: $grid-unit-80 * 4; - .components-button.has-icon { - // Hide the button icons when labels are set to display... - svg { - display: none; - } - // ... and display labels. - &::after { - content: attr(aria-label); - font-size: $helptext-font-size; - } - } - } - - // Add a border as separator in the block toolbar. - &::before { - content: ""; - width: $border-width; - margin-top: $grid-unit + $grid-unit-05; - margin-bottom: $grid-unit + $grid-unit-05; - background-color: $gray-300; - position: relative; - left: -8px; - height: 3 * $grid-unit; - top: -1px; - } - } - - .show-icon-labels & { - .block-editor-block-parent-selector .block-editor-block-parent-selector__button::after { - left: 0; - } - - .block-editor-block-toolbar__block-controls .block-editor-block-mover { - border-left: none; - &::before { - content: ""; - width: $border-width; - margin-top: $grid-unit + $grid-unit-05; - margin-bottom: $grid-unit + $grid-unit-05; - background-color: $gray-300; - position: relative; - } - } - } - } - - &.is-fixed .block-editor-block-parent-selector { - - .block-editor-block-parent-selector__button { - position: relative; - top: -1px; - border: 0; - padding-right: 6px; - padding-left: 6px; - - &::after { - content: "\00B7"; - font-size: 16px; - line-height: $grid-unit-40 + $grid-unit-10; - position: absolute; - left: $grid-unit-40 + $grid-unit-15 + 2px; - bottom: $grid-unit-05; - } - } - } - - &:not(.is-fixed) .block-editor-block-parent-selector { - position: absolute; - top: -$border-width; - left: calc(-#{$grid-unit-60} - #{$grid-unit-10} - #{$border-width}); - - .show-icon-labels & { - position: relative; - left: auto; - top: auto; - margin-top: -$border-width; - margin-left: -$border-width; - margin-bottom: -$border-width; - } - } - } -} - /** * Block Label for Navigation/Selection Mode */ @@ -349,6 +177,7 @@ } .components-popover.block-editor-block-list__block-popover { + // Position the block toolbar. .block-editor-block-list__block-selection-button, .block-editor-block-contextual-toolbar { @@ -357,6 +186,30 @@ margin-bottom: $grid-unit-15; } + .block-editor-block-contextual-toolbar { + border: $border-width solid $gray-900; + border-radius: $radius-block-ui; + overflow: visible; // allow the parent selector to be visible + position: static; + width: auto; + + &.has-parent { + margin-left: calc(#{$grid-unit-60} + #{$grid-unit-10}); + .show-icon-labels & { + margin-left: 0; + } + } + } + + .block-editor-block-toolbar { + overflow: visible; + } + + .block-editor-block-toolbar .components-toolbar-group, + .block-editor-block-toolbar .components-toolbar { + border-right-color: $gray-900; + } + // Hide the block toolbar if the insertion point is shown. &.is-insertion-point-visible { visibility: hidden; @@ -368,6 +221,41 @@ // It's essential to hide the toolbar/popover so that `dragEnter` events can pass through them to the underlying elements. animation: hide-during-dragging 1ms linear forwards; } + + .block-editor-block-parent-selector { + position: absolute; + left: calc(-#{$grid-unit-60} - #{$grid-unit-10} - #{$border-width}); + + &::before { + content: ""; + } + + .block-editor-block-parent-selector__button { + border: 1px solid $gray-900; + padding-right: 6px; + padding-left: 6px; + background-color: $white; + } + } + + // Show Icon Label Styles + .show-icon-labels & { + + .block-editor-block-parent-selector { + position: static; + margin-top: -$border-width; + margin-left: -$border-width; + margin-bottom: -$border-width; + + .block-editor-block-parent-selector__button { + position: static; + } + } + .block-editor-block-mover__move-button-container, + .block-editor-block-toolbar__block-controls .block-editor-block-mover { + border-left: 1px solid $gray-900; + } + } } .is-dragging-components-draggable .components-tooltip { diff --git a/packages/block-editor/src/components/navigable-toolbar/README.md b/packages/block-editor/src/components/navigable-toolbar/README.md index 30a4d100195f85..317be48f38faa2 100644 --- a/packages/block-editor/src/components/navigable-toolbar/README.md +++ b/packages/block-editor/src/components/navigable-toolbar/README.md @@ -8,6 +8,8 @@ The component accepts the following props. Props not included in this set will b ## `focusOnMount` +_Note: this prop is deprecated._ + Whether to immediately focus when the component mounts. - Type: `Boolean` diff --git a/packages/block-editor/src/components/navigable-toolbar/index.js b/packages/block-editor/src/components/navigable-toolbar/index.js index fe216e1058f6f0..8954f7e17f132e 100644 --- a/packages/block-editor/src/components/navigable-toolbar/index.js +++ b/packages/block-editor/src/components/navigable-toolbar/index.js @@ -162,7 +162,7 @@ function useToolbarFocus( { const index = items.findIndex( ( item ) => item.tabIndex === 0 ); onIndexChange( index ); }; - }, [ initialIndex, initialFocusOnMount, toolbarRef ] ); + }, [ initialIndex, initialFocusOnMount, onIndexChange, toolbarRef ] ); const { lastFocus } = useSelect( ( select ) => { const { getLastFocus } = select( blockEditorStore ); @@ -210,9 +210,9 @@ export default function NavigableToolbar( { useToolbarFocus( { toolbarRef, focusOnMount, - isAccessibleToolbar, defaultIndex: initialIndex, onIndexChange, + isAccessibleToolbar, shouldUseKeyboardFocusShortcut, focusEditorOnEscape, } ); diff --git a/packages/block-editor/src/private-apis.js b/packages/block-editor/src/private-apis.js index b1d499ae099469..9837c206487bea 100644 --- a/packages/block-editor/src/private-apis.js +++ b/packages/block-editor/src/private-apis.js @@ -10,7 +10,6 @@ import ResizableBoxPopover from './components/resizable-box-popover'; import { ComposedPrivateInserter as PrivateInserter } from './components/inserter'; import { PrivateListView } from './components/list-view'; import BlockInfo from './components/block-info-slot-fill'; -import BlockContextualToolbar from './components/block-tools/block-contextual-toolbar'; import { useShouldContextualToolbarShow } from './utils/use-should-contextual-toolbar-show'; import { cleanEmptyObject, useStyleOverride } from './hooks/utils'; import BlockQuickNavigation from './components/block-quick-navigation'; @@ -42,7 +41,6 @@ lock( privateApis, { PrivateListView, ResizableBoxPopover, BlockInfo, - BlockContextualToolbar, useShouldContextualToolbarShow, cleanEmptyObject, useStyleOverride, diff --git a/packages/block-editor/src/style.scss b/packages/block-editor/src/style.scss index a55756ae6f53d7..16de2dfdb71142 100644 --- a/packages/block-editor/src/style.scss +++ b/packages/block-editor/src/style.scss @@ -10,7 +10,6 @@ @import "./components/block-draggable/style.scss"; @import "./components/block-mover/style.scss"; @import "./components/block-navigation/style.scss"; -@import "./components/block-parent-selector/style.scss"; @import "./components/block-patterns-list/style.scss"; @import "./components/block-patterns-paging/style.scss"; @import "./components/block-popover/style.scss"; diff --git a/packages/customize-widgets/src/components/header/index.js b/packages/customize-widgets/src/components/header/index.js index 34e4573c719dd5..5bd0b2c2f4d471 100644 --- a/packages/customize-widgets/src/components/header/index.js +++ b/packages/customize-widgets/src/components/header/index.js @@ -6,13 +6,9 @@ import classnames from 'classnames'; /** * WordPress dependencies */ -import { Popover, ToolbarButton } from '@wordpress/components'; -import { useViewportMatch } from '@wordpress/compose'; -import { - NavigableToolbar, - privateApis as blockEditorPrivateApis, -} from '@wordpress/block-editor'; -import { createPortal, useEffect, useRef, useState } from '@wordpress/element'; +import { ToolbarButton } from '@wordpress/components'; +import { NavigableToolbar } from '@wordpress/block-editor'; +import { createPortal, useEffect, useState } from '@wordpress/element'; import { displayShortcut, isAppleOS } from '@wordpress/keycodes'; import { __, _x, isRTL } from '@wordpress/i18n'; import { plus, undo as undoIcon, redo as redoIcon } from '@wordpress/icons'; @@ -22,9 +18,6 @@ import { plus, undo as undoIcon, redo as redoIcon } from '@wordpress/icons'; */ import Inserter from '../inserter'; import MoreMenu from '../more-menu'; -import { unlock } from '../../lock-unlock'; - -const { BlockContextualToolbar } = unlock( blockEditorPrivateApis ); function Header( { sidebar, @@ -33,8 +26,6 @@ function Header( { setIsInserterOpened, isFixedToolbarActive, } ) { - const isLargeViewport = useViewportMatch( 'medium' ); - const blockToolbarRef = useRef(); const [ [ hasUndo, hasRedo ], setUndoRedo ] = useState( [ sidebar.hasUndo(), sidebar.hasRedo(), @@ -107,18 +98,6 @@ function Header( { , inserter.contentContainer[ 0 ] ) } - - { isFixedToolbarActive && isLargeViewport && ( - <> -
- -
- - - ) } ); } diff --git a/packages/customize-widgets/src/components/header/style.scss b/packages/customize-widgets/src/components/header/style.scss index d9d4a487e647c1..27460a82e0ad10 100644 --- a/packages/customize-widgets/src/components/header/style.scss +++ b/packages/customize-widgets/src/components/header/style.scss @@ -1,5 +1,5 @@ .customize-widgets-header { - @include break-medium() { + @include break-small() { // Make space for the floating toolbar. margin-bottom: $grid-unit-20 + $default-block-margin; } diff --git a/packages/customize-widgets/src/components/sidebar-block-editor/index.js b/packages/customize-widgets/src/components/sidebar-block-editor/index.js index ccb6fca871429e..c2e10bca16ec0b 100644 --- a/packages/customize-widgets/src/components/sidebar-block-editor/index.js +++ b/packages/customize-widgets/src/components/sidebar-block-editor/index.js @@ -1,11 +1,13 @@ /** * WordPress dependencies */ +import { useViewportMatch } from '@wordpress/compose'; import { store as coreStore } from '@wordpress/core-data'; import { useSelect } from '@wordpress/data'; import { useMemo, createPortal } from '@wordpress/element'; import { BlockList, + BlockToolbar, BlockTools, BlockInspector, privateApis as blockEditorPrivateApis, @@ -37,6 +39,7 @@ export default function SidebarBlockEditor( { inspector, } ) { const [ isInserterOpened, setIsInserterOpened ] = useInserter( inserter ); + const isMediumViewport = useViewportMatch( 'small' ); const { hasUploadPermissions, isFixedToolbarActive, @@ -77,7 +80,7 @@ export default function SidebarBlockEditor( { ...blockEditorSettings, __experimentalSetIsInserterOpened: setIsInserterOpened, mediaUpload: mediaUploadBlockEditor, - hasFixedToolbar: isFixedToolbarActive, + hasFixedToolbar: isFixedToolbarActive || ! isMediumViewport, keepCaretInsideBlock, __unstableHasCustomAppender: true, }; @@ -85,6 +88,7 @@ export default function SidebarBlockEditor( { hasUploadPermissions, blockEditorSettings, isFixedToolbarActive, + isMediumViewport, keepCaretInsideBlock, setIsInserterOpened, ] ); @@ -109,9 +113,13 @@ export default function SidebarBlockEditor( { inserter={ inserter } isInserterOpened={ isInserterOpened } setIsInserterOpened={ setIsInserterOpened } - isFixedToolbarActive={ isFixedToolbarActive } + isFixedToolbarActive={ + isFixedToolbarActive || ! isMediumViewport + } /> - + { ( isFixedToolbarActive || ! isMediumViewport ) && ( + + ) } .block-editor-block-toolbar__group-collapse-fixed-toolbar { - display: none; - } - - // Scroll sideways. - overflow-y: auto; - z-index: z-index(".customize-widgets__block-toolbar"); -} - .customize-control-sidebar_block_editor .block-editor-block-list__block-popover { // FloatingUI library used in Popover component forces us to have an "absolute" inline style. // We need to override this in the customizer. diff --git a/packages/edit-post/src/components/header/index.js b/packages/edit-post/src/components/header/index.js index 8bfeb2d253ce11..b92c6c44fe49f3 100644 --- a/packages/edit-post/src/components/header/index.js +++ b/packages/edit-post/src/components/header/index.js @@ -7,7 +7,7 @@ import classnames from 'classnames'; * WordPress dependencies */ import { - privateApis as blockEditorPrivateApis, + BlockToolbar, store as blockEditorStore, } from '@wordpress/block-editor'; import { @@ -40,9 +40,6 @@ import { default as DevicePreview } from '../device-preview'; import ViewLink from '../view-link'; import MainDashboardButton from './main-dashboard-button'; import { store as editPostStore } from '../../store'; -import { unlock } from '../../lock-unlock'; - -const { BlockContextualToolbar } = unlock( blockEditorPrivateApis ); const slideY = { hidden: { y: '-50px' }, @@ -130,7 +127,7 @@ function Header( { } ) } > - +
=' ); - const isLargeViewport = useViewportMatch( 'large' ); + const isWideViewport = useViewportMatch( 'large' ); + const isLargeViewport = useViewportMatch( 'medium' ); + const { openGeneralSidebar, closeGeneralSidebar, setIsInserterOpened } = useDispatch( editPostStore ); const { createErrorNotice } = useDispatch( noticesStore ); @@ -148,7 +151,6 @@ function Layout() { isRichEditingEnabled, sidebarIsOpened, hasActiveMetaboxes, - hasFixedToolbar, previousShortcut, nextShortcut, hasBlockSelected, @@ -167,8 +169,6 @@ function Layout() { return { showMetaBoxes: select( editorStore ).getRenderingMode() === 'post-only', - hasFixedToolbar: - select( editPostStore ).isFeatureActive( 'fixedToolbar' ), sidebarIsOpened: !! ( select( interfaceStore ).getActiveComplementaryArea( editPostStore.name @@ -219,12 +219,12 @@ function Layout() { if ( sidebarIsOpened && ! isHugeViewport ) { setIsInserterOpened( false ); } - }, [ sidebarIsOpened, isHugeViewport ] ); + }, [ isHugeViewport, setIsInserterOpened, sidebarIsOpened ] ); useEffect( () => { if ( isInserterOpened && ! isHugeViewport ) { closeGeneralSidebar(); } - }, [ isInserterOpened, isHugeViewport ] ); + }, [ closeGeneralSidebar, isInserterOpened, isHugeViewport ] ); // Local state for save panel. // Note 'truthy' callback implies an open panel. @@ -253,9 +253,8 @@ function Layout() { const className = classnames( 'edit-post-layout', 'is-mode-' + mode, { 'is-sidebar-opened': sidebarIsOpened, - 'has-fixed-toolbar': hasFixedToolbar, 'has-metaboxes': hasActiveMetaboxes, - 'is-distraction-free': isDistractionFree && isLargeViewport, + 'is-distraction-free': isDistractionFree && isWideViewport, 'is-entity-save-view-open': !! entitiesSavedStatesCallback, } ); @@ -302,7 +301,7 @@ function Layout() { ) } + { ! isLargeViewport && } { isRichEditingEnabled && mode === 'visual' && ( ) } diff --git a/packages/edit-post/src/editor.js b/packages/edit-post/src/editor.js index cff867c3f7a2cb..0abf3328635a86 100644 --- a/packages/edit-post/src/editor.js +++ b/packages/edit-post/src/editor.js @@ -14,6 +14,7 @@ import { SlotFillProvider } from '@wordpress/components'; import { store as coreStore } from '@wordpress/core-data'; import { store as preferencesStore } from '@wordpress/preferences'; import { CommandMenu } from '@wordpress/commands'; +import { useViewportMatch } from '@wordpress/compose'; /** * Internal dependencies @@ -26,6 +27,8 @@ import { unlock } from './lock-unlock'; const { ExperimentalEditorProvider } = unlock( editorPrivateApis ); function Editor( { postId, postType, settings, initialEdits, ...props } ) { + const isLargeViewport = useViewportMatch( 'medium' ); + const { hasFixedToolbar, focusMode, @@ -66,9 +69,9 @@ function Editor( { postId, postType, settings, initialEdits, ...props } ) { getEditorSettings().supportsTemplateMode; const isViewable = getPostType( postType )?.viewable ?? false; const canEditTemplate = canUser( 'create', 'templates' ); - return { - hasFixedToolbar: isFeatureActive( 'fixedToolbar' ), + hasFixedToolbar: + isFeatureActive( 'fixedToolbar' ) || ! isLargeViewport, focusMode: isFeatureActive( 'focusMode' ), isDistractionFree: isFeatureActive( 'distractionFree' ), hasInlineToolbar: isFeatureActive( 'inlineToolbar' ), @@ -86,7 +89,7 @@ function Editor( { postId, postType, settings, initialEdits, ...props } ) { post: postObject, }; }, - [ postType, postId ] + [ postType, postId, isLargeViewport ] ); const { updatePreferredStyleVariations, setIsInserterOpened } = diff --git a/packages/edit-site/src/components/block-editor/style.scss b/packages/edit-site/src/components/block-editor/style.scss index e02240eb880992..1da43730d95753 100644 --- a/packages/edit-site/src/components/block-editor/style.scss +++ b/packages/edit-site/src/components/block-editor/style.scss @@ -69,17 +69,6 @@ &.is-view-mode { box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.8), 0 8px 10px -6px rgba(0, 0, 0, 0.8); - - /* - Temporary to hide the contextual toolbar in view mode. - See: https://github.com/WordPress/gutenberg/pull/46298 - This rule can possibly be removed once the - contextual toolbar has been redesigned. - See: https://github.com/WordPress/gutenberg/issues/40450 - */ - .block-editor-block-contextual-toolbar.is-fixed { - display: none; - } } } diff --git a/packages/edit-site/src/components/block-editor/use-site-editor-settings.js b/packages/edit-site/src/components/block-editor/use-site-editor-settings.js index 962cfe09afb720..2deb2d4cb5fa6e 100644 --- a/packages/edit-site/src/components/block-editor/use-site-editor-settings.js +++ b/packages/edit-site/src/components/block-editor/use-site-editor-settings.js @@ -1,6 +1,7 @@ /** * WordPress dependencies */ +import { useViewportMatch } from '@wordpress/compose'; import { useDispatch, useSelect } from '@wordpress/data'; import { useMemo } from '@wordpress/element'; import { store as coreStore } from '@wordpress/core-data'; @@ -89,6 +90,7 @@ function useArchiveLabel( templateSlug ) { export function useSpecificEditorSettings() { const { setIsInserterOpened } = useDispatch( editSiteStore ); + const isLargeViewport = useViewportMatch( 'medium' ); const { templateSlug, focusMode, @@ -98,44 +100,46 @@ export function useSpecificEditorSettings() { canvasMode, settings, postWithTemplate, - } = useSelect( ( select ) => { - const { - getEditedPostType, - getEditedPostId, - getEditedPostContext, - getCanvasMode, - getSettings, - } = unlock( select( editSiteStore ) ); - const { get: getPreference } = select( preferencesStore ); - const { getEditedEntityRecord } = select( coreStore ); - const usedPostType = getEditedPostType(); - const usedPostId = getEditedPostId(); - const _record = getEditedEntityRecord( - 'postType', - usedPostType, - usedPostId - ); - const _context = getEditedPostContext(); - return { - templateSlug: _record.slug, - focusMode: !! getPreference( 'core/edit-site', 'focusMode' ), - isDistractionFree: !! getPreference( - 'core/edit-site', - 'distractionFree' - ), - hasFixedToolbar: !! getPreference( - 'core/edit-site', - 'fixedToolbar' - ), - keepCaretInsideBlock: !! getPreference( - 'core/edit-site', - 'keepCaretInsideBlock' - ), - canvasMode: getCanvasMode(), - settings: getSettings(), - postWithTemplate: _context?.postId, - }; - }, [] ); + } = useSelect( + ( select ) => { + const { + getEditedPostType, + getEditedPostId, + getEditedPostContext, + getCanvasMode, + getSettings, + } = unlock( select( editSiteStore ) ); + const { get: getPreference } = select( preferencesStore ); + const { getEditedEntityRecord } = select( coreStore ); + const usedPostType = getEditedPostType(); + const usedPostId = getEditedPostId(); + const _record = getEditedEntityRecord( + 'postType', + usedPostType, + usedPostId + ); + const _context = getEditedPostContext(); + return { + templateSlug: _record.slug, + focusMode: !! getPreference( 'core/edit-site', 'focusMode' ), + isDistractionFree: !! getPreference( + 'core/edit-site', + 'distractionFree' + ), + hasFixedToolbar: + !! getPreference( 'core/edit-site', 'fixedToolbar' ) || + ! isLargeViewport, + keepCaretInsideBlock: !! getPreference( + 'core/edit-site', + 'keepCaretInsideBlock' + ), + canvasMode: getCanvasMode(), + settings: getSettings(), + postWithTemplate: _context?.postId, + }; + }, + [ isLargeViewport ] + ); const archiveLabels = useArchiveLabel( templateSlug ); const defaultRenderingMode = postWithTemplate ? 'template-locked' : 'all'; const defaultEditorSettings = useMemo( () => { diff --git a/packages/edit-site/src/components/editor/index.js b/packages/edit-site/src/components/editor/index.js index 5a2f1e2ec4d1a9..295f4ec3cf5c60 100644 --- a/packages/edit-site/src/components/editor/index.js +++ b/packages/edit-site/src/components/editor/index.js @@ -8,10 +8,11 @@ import classnames from 'classnames'; */ import { useSelect } from '@wordpress/data'; import { Notice } from '@wordpress/components'; -import { useInstanceId } from '@wordpress/compose'; +import { useInstanceId, useViewportMatch } from '@wordpress/compose'; import { store as preferencesStore } from '@wordpress/preferences'; import { BlockBreadcrumb, + BlockToolbar, store as blockEditorStore, privateApis as blockEditorPrivateApis, BlockInspector, @@ -92,6 +93,8 @@ export default function Editor( { listViewToggleElement, isLoading } ) { const { type: editedPostType } = editedPost; + const isLargeViewport = useViewportMatch( 'medium' ); + const { context, contextPost, @@ -232,6 +235,9 @@ export default function Editor( { listViewToggleElement, isLoading } ) { + { ! isLargeViewport && ( + + ) } - + * + * { margin-left: $grid-unit-10; } + + .block-editor-block-mover { + border-left: none; + + &::before { + content: ""; + width: $border-width; + margin-top: $grid-unit + $grid-unit-05; + margin-bottom: $grid-unit + $grid-unit-05; + background-color: $gray-300; + margin-left: $grid-unit; + } + } } .has-fixed-toolbar { .selected-block-tools-wrapper { overflow-x: scroll; + .block-editor-block-contextual-toolbar { + border-bottom: 0; + } + + // Modified group borders + .components-toolbar-group, + .components-toolbar { + border-right: none; + + &::after { + content: ""; + width: $border-width; + margin-top: $grid-unit + $grid-unit-05; + margin-bottom: $grid-unit + $grid-unit-05; + background-color: $gray-300; + margin-left: $grid-unit; + } + + & .components-toolbar-group.components-toolbar-group { + &::after { + display: none; + } + } + } + &.is-collapsed { display: none; } diff --git a/packages/edit-widgets/src/components/header/index.js b/packages/edit-widgets/src/components/header/index.js index 9251f528ca5ee4..9d4cb4cb60103a 100644 --- a/packages/edit-widgets/src/components/header/index.js +++ b/packages/edit-widgets/src/components/header/index.js @@ -1,7 +1,7 @@ /** * WordPress dependencies */ -import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor'; +import { BlockToolbar } from '@wordpress/block-editor'; import { useSelect } from '@wordpress/data'; import { useRef } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; @@ -16,9 +16,6 @@ import { store as preferencesStore } from '@wordpress/preferences'; import DocumentTools from './document-tools'; import SaveButton from '../save-button'; import MoreMenu from '../more-menu'; -import { unlock } from '../../lock-unlock'; - -const { BlockContextualToolbar } = unlock( blockEditorPrivateApis ); function Header( { setListViewToggleElement } ) { const isLargeViewport = useViewportMatch( 'medium' ); @@ -56,7 +53,7 @@ function Header( { setListViewToggleElement } ) { { hasFixedToolbar && isLargeViewport && ( <>
- +
{ return hasThemeStyles ? blockEditorSettings.styles : []; @@ -37,6 +40,7 @@ export default function WidgetAreasBlockEditorContent( { return (
+ { ! isLargeViewport && } + diff --git a/storybook/stories/playground/with-undo-redo/index.js b/storybook/stories/playground/with-undo-redo/index.js index a51d8624282a6d..537ea16aade99b 100644 --- a/storybook/stories/playground/with-undo-redo/index.js +++ b/storybook/stories/playground/with-undo-redo/index.js @@ -7,6 +7,7 @@ import { registerCoreBlocks } from '@wordpress/block-library'; import { BlockEditorProvider, BlockCanvas, + BlockToolbar, BlockTools, } from '@wordpress/block-editor'; import { Button } from '@wordpress/components'; @@ -58,6 +59,7 @@ export default function EditorWithUndoRedo() { icon={ redoIcon } label="Redo" /> +
diff --git a/storybook/stories/playground/with-undo-redo/style.css b/storybook/stories/playground/with-undo-redo/style.css index a3f0bd5d23debf..6ed082a1de7196 100644 --- a/storybook/stories/playground/with-undo-redo/style.css +++ b/storybook/stories/playground/with-undo-redo/style.css @@ -6,5 +6,9 @@ display: flex; align-items: center; border-bottom: 1px solid #ddd; - height: 48px; + height: 46px; } + +.editor-with-undo-redo__toolbar .components-accessible-toolbar.block-editor-block-contextual-toolbar { + border-bottom: none; +} \ No newline at end of file diff --git a/test/e2e/specs/editor/various/is-typing.spec.js b/test/e2e/specs/editor/various/is-typing.spec.js index 0cd5e0d6f64953..8063f688409c4e 100644 --- a/test/e2e/specs/editor/various/is-typing.spec.js +++ b/test/e2e/specs/editor/various/is-typing.spec.js @@ -14,24 +14,27 @@ test.describe( 'isTyping', () => { // Insert paragraph await page.keyboard.type( 'Type' ); - const blockToolbar = page.locator( - 'role=toolbar[name="Block tools"i]' + const blockToolbarPopover = page.locator( + '[data-wp-component="Popover"]', + { + has: page.locator( 'role=toolbar[name="Block tools"i]' ), + } ); - // Toolbar should not be showing - await expect( blockToolbar ).toBeHidden(); + // Toolbar Popover should not be showing + await expect( blockToolbarPopover ).toBeHidden(); // Moving the mouse shows the toolbar. await editor.showBlockToolbar(); - // Toolbar is visible. - await expect( blockToolbar ).toBeVisible(); + // Toolbar Popover is visible. + await expect( blockToolbarPopover ).toBeVisible(); // Typing again hides the toolbar await page.keyboard.type( ' and continue' ); - // Toolbar is hidden again - await expect( blockToolbar ).toBeHidden(); + // Toolbar Popover is hidden again + await expect( blockToolbarPopover ).toBeHidden(); } ); test( 'should not close the dropdown when typing in it', async ( { @@ -41,17 +44,22 @@ test.describe( 'isTyping', () => { // Add a block with a dropdown in the toolbar that contains an input. await editor.insertBlock( { name: 'core/query' } ); - // Tab to Start Blank Button - await page.keyboard.press( 'Tab' ); - // Select the Start Blank Button - await page.keyboard.press( 'Enter' ); - // Select the First variation - await page.keyboard.press( 'Enter' ); + await editor.canvas + .getByRole( 'document', { name: 'Block: Query Loop' } ) + .getByRole( 'button', { name: 'Start blank' } ) + .click(); + + await editor.canvas + .getByRole( 'button', { name: 'Title & Date' } ) + .click(); + // Moving the mouse shows the toolbar. await editor.showBlockToolbar(); // Open the dropdown. - await page.getByRole( 'button', { name: 'Display settings' } ).click(); - + const displaySettings = page.getByRole( 'button', { + name: 'Display settings', + } ); + await displaySettings.click(); const itemsPerPageInput = page.getByLabel( 'Items per Page' ); // Make sure we're where we think we are await expect( itemsPerPageInput ).toBeFocused(); diff --git a/test/e2e/specs/editor/various/shortcut-focus-toolbar.spec.js b/test/e2e/specs/editor/various/shortcut-focus-toolbar.spec.js index 0223821613f55e..a8e49f7a6b84dd 100644 --- a/test/e2e/specs/editor/various/shortcut-focus-toolbar.spec.js +++ b/test/e2e/specs/editor/various/shortcut-focus-toolbar.spec.js @@ -98,6 +98,10 @@ test.describe( 'Focus toolbar shortcut (alt + F10)', () => { // Test: Focus the block toolbar from empty block await editor.insertBlock( { name: 'core/paragraph' } ); + // This fails if we don't wait for the block toolbar to show. + await expect( + toolbarUtils.blockToolbarParagraphButton + ).toBeVisible(); await toolbarUtils.moveToToolbarShortcut(); await expect( toolbarUtils.blockToolbarParagraphButton