Skip to content

Commit

Permalink
Refactor <BlockToolbar />: Add variant prop and <NavigableToolbar /> …
Browse files Browse the repository at this point in the history
…wrapper (WordPress#56335)

- Combine `<BlockContextualToolbar />` into `<BlockToolbar />`. This brings all the necessary functionality for `<BlockToolbar />` to be used, mainly by wrapping it in a `<NavigableToolbar />`.
- Remove `<BlockContextualToolbar />`. This is no longer needed, and was originally intended for Contextual to mean Popover.
- Replace usage of `<BlockContextualToobar />` in edit-site, edit-post, edit-widget, etc headers with `<BlockToolbar />`
- Refactor `<SelectedBlockTools />` to become `<BlockToolbarPopover />` to better reflect its purpose.
- Add `<PrivateBlockToolbar />` with new props of `__experimentalInitialIndex`, `__experimentalOnIndexChange`, and `focusOnMount`.
- Export `<BlockToolbar />` from  `<PrivateBlockToolbar />` 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 `<BlockToolbar />`. 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 `<BlockToolbarPopover />` and `<BlockBreadcrumbPopover />` into separate components for simplicity.
- Top Toolbar now means. You have to implement the `<BlockToolbar />` on your own.
  • Loading branch information
jeryj authored Dec 7, 2023
1 parent f48ac05 commit b45f22c
Show file tree
Hide file tree
Showing 37 changed files with 725 additions and 759 deletions.
8 changes: 8 additions & 0 deletions packages/block-editor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -280,10 +280,18 @@ _Returns_

### BlockToolbar

Renders the block toolbar.

_Related_

- <https://github.com/WordPress/gutenberg/blob/HEAD/packages/block-editor/src/components/block-toolbar/README.md>

_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.
Expand Down

This file was deleted.

275 changes: 180 additions & 95 deletions packages/block-editor/src/components/block-toolbar/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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 );

Expand All @@ -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 (
<div className={ classes } ref={ toolbarWrapperRef }>
{ ! isMultiToolbar &&
isLargeViewport &&
blockEditingMode === 'default' && <BlockParentSelector /> }
{ ( shouldShowVisualToolbar || isMultiToolbar ) &&
blockEditingMode === 'default' && (
<div ref={ nodeRef } { ...showHoveredOrFocusedGestures }>
<ToolbarGroup className="block-editor-block-toolbar__block-controls">
<BlockSwitcher clientIds={ blockClientIds } />
{ ! isMultiToolbar && (
<BlockLockToolbar
clientId={ blockClientIds[ 0 ] }
wrapperRef={ toolbarWrapperRef }
<NavigableToolbar
focusEditorOnEscape
className={ classes }
/* translators: accessibility text for the block toolbar */
aria-label={ __( 'Block tools' ) }
// The variant is applied as "toolbar" when undefined, which is the black border style of the dropdown from the toolbar popover.
variant={ variant === 'toolbar' ? undefined : variant }
focusOnMount={ focusOnMount }
__experimentalInitialIndex={ __experimentalInitialIndex }
__experimentalOnIndexChange={ __experimentalOnIndexChange }
// Resets the index whenever the active block changes so
// this is not persisted. See https://github.com/WordPress/gutenberg/pull/25760#issuecomment-717906169
key={ blockClientId }
>
<div ref={ toolbarWrapperRef } className={ innerClasses }>
{ ! isMultiToolbar &&
isLargeViewport &&
isDefaultEditingMode && <BlockParentSelector /> }
{ ( shouldShowVisualToolbar || isMultiToolbar ) &&
isDefaultEditingMode && (
<div
ref={ nodeRef }
{ ...showHoveredOrFocusedGestures }
>
<ToolbarGroup className="block-editor-block-toolbar__block-controls">
<BlockSwitcher clientIds={ blockClientIds } />
{ ! isMultiToolbar && (
<BlockLockToolbar
clientId={ blockClientIds[ 0 ] }
wrapperRef={ toolbarWrapperRef }
/>
) }
<BlockMover
clientIds={ blockClientIds }
hideDragHandle={ hideDragHandle }
/>
) }
<BlockMover
clientIds={ blockClientIds }
hideDragHandle={ hideDragHandle }
/>
</ToolbarGroup>
</div>
</ToolbarGroup>
</div>
) }
{ shouldShowVisualToolbar && isMultiToolbar && (
<BlockGroupToolbar />
) }
{ shouldShowVisualToolbar && (
<>
<BlockControls.Slot
group="parent"
className="block-editor-block-toolbar__slot"
/>
<BlockControls.Slot
group="block"
className="block-editor-block-toolbar__slot"
/>
<BlockControls.Slot className="block-editor-block-toolbar__slot" />
<BlockControls.Slot
group="inline"
className="block-editor-block-toolbar__slot"
/>
<BlockControls.Slot
group="other"
className="block-editor-block-toolbar__slot"
/>
<__unstableBlockNameContext.Provider
value={ blockType?.name }
>
<__unstableBlockToolbarLastItem.Slot />
</__unstableBlockNameContext.Provider>
</>
) }
<BlockEditVisuallyButton clientIds={ blockClientIds } />
{ isDefaultEditingMode && (
<BlockSettingsMenu clientIds={ blockClientIds } />
) }
{ shouldShowVisualToolbar && isMultiToolbar && (
<BlockGroupToolbar />
) }
{ shouldShowVisualToolbar && (
<>
<BlockControls.Slot
group="parent"
className="block-editor-block-toolbar__slot"
/>
<BlockControls.Slot
group="block"
className="block-editor-block-toolbar__slot"
/>
<BlockControls.Slot className="block-editor-block-toolbar__slot" />
<BlockControls.Slot
group="inline"
className="block-editor-block-toolbar__slot"
/>
<BlockControls.Slot
group="other"
className="block-editor-block-toolbar__slot"
/>
<__unstableBlockNameContext.Provider
value={ blockType?.name }
>
<__unstableBlockToolbarLastItem.Slot />
</__unstableBlockNameContext.Provider>
</>
) }
<BlockEditVisuallyButton clientIds={ blockClientIds } />
{ blockEditingMode === 'default' && (
<BlockSettingsMenu clientIds={ blockClientIds } />
) }
</div>
</div>
</NavigableToolbar>
);
};
}

/**
* 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 (
<PrivateBlockToolbar
hideDragHandle={ hideDragHandle }
variant={ variant }
focusOnMount={ undefined }
__experimentalInitialIndex={ undefined }
__experimentalOnIndexChange={ undefined }
/>
);
}
Loading

0 comments on commit b45f22c

Please sign in to comment.