Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stacked/unified block toolbar #31134

Merged
merged 3 commits into from
May 7, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 19 additions & 6 deletions packages/block-editor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ _This package assumes that your code will run in an **ES2015+** environment. If
import {
BlockEditorProvider,
BlockList,
BlockTools,
WritingFlow,
ObserveTyping,
} from '@wordpress/block-editor';
Expand All @@ -34,12 +35,13 @@ function MyEditorComponent() {
onChange={ ( blocks ) => updateBlocks( blocks ) }
>
<SlotFillProvider>
<Popover.Slot name="block-toolbar" />
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@youknowriad I think you'll be happy to see this 😄

<WritingFlow>
<ObserveTyping>
<BlockList />
</ObserveTyping>
</WritingFlow>
<BlockTools>
<WritingFlow>
<ObserveTyping>
<BlockList />
</ObserveTyping>
</WritingFlow>
</BlockTools>
<Popover.Slot />
</SlotFillProvider>
</BlockEditorProvider>
Expand Down Expand Up @@ -226,6 +228,17 @@ _Returns_

Undocumented declaration.

<a name="BlockTools" href="#BlockTools">#</a> **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.

_Parameters_

- _$0_ `Object`: Props.
- _$0.children_ `Object`: The block content and style container.

<a name="BlockVerticalAlignmentControl" href="#BlockVerticalAlignmentControl">#</a> **BlockVerticalAlignmentControl**

Undocumented declaration.
Expand Down
6 changes: 3 additions & 3 deletions packages/block-editor/src/components/block-list/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ import BlockListBlock from './block';
import BlockListAppender from '../block-list-appender';
import useBlockDropZone from '../use-block-drop-zone';
import { useInBetweenInserter } from './use-in-between-inserter';
import BlockTools from '../block-tools';
import { store as blockEditorStore } from '../../store';
import { usePreParsePatterns } from '../../utils/pre-parse-patterns';
import { LayoutProvider, defaultLayout } from './layout';
import BlockToolsBackCompat from '../block-tools/back-compat';

function Root( { className, children } ) {
const isLargeViewport = useViewportMatch( 'medium' );
Expand Down Expand Up @@ -67,11 +67,11 @@ function Root( { className, children } ) {
export default function BlockList( { className, __experimentalLayout } ) {
usePreParsePatterns();
return (
<BlockTools>
<BlockToolsBackCompat>
<Root className={ className }>
<BlockListItems __experimentalLayout={ __experimentalLayout } />
</Root>
</BlockTools>
</BlockToolsBackCompat>
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
}
}

.block-editor-block-contextual-toolbar.has-parent {
.block-editor-block-contextual-toolbar.has-parent:not(.is-fixed) {
margin-left: calc(#{$grid-unit-60} + #{$grid-unit-10});

.show-icon-labels & {
Expand Down
33 changes: 33 additions & 0 deletions packages/block-editor/src/components/block-tools/back-compat.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* WordPress dependencies
*/
import { useContext } from '@wordpress/element';
import { Disabled } from '@wordpress/components';
import deprecated from '@wordpress/deprecated';

/**
* Internal dependencies
*/
import InsertionPoint, { InsertionPointOpenRef } from './insertion-point';
import BlockPopover from './block-popover';

export default function BlockToolsBackCompat( { children } ) {
const openRef = useContext( InsertionPointOpenRef );
const isDisabled = useContext( Disabled.Context );

// If context is set, `BlockTools` is a parent component.
if ( openRef || isDisabled ) {
return children;
}

deprecated( 'wp.components.Popover.Slot name="block-toolbar"', {
alternative: 'wp.blockEditor.BlockTools',
} );

return (
<InsertionPoint __unstablePopoverSlot="block-toolbar">
<BlockPopover __unstablePopoverSlot="block-toolbar" />
{ children }
</InsertionPoint>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ import { useSelect } from '@wordpress/data';
* Internal dependencies
*/
import NavigableToolbar from '../navigable-toolbar';
import { BlockToolbar } from '../';
import BlockToolbar from '../block-toolbar';
import { store as blockEditorStore } from '../../store';

function BlockContextualToolbar( { focusOnMount, ...props } ) {
function BlockContextualToolbar( { focusOnMount, isFixed, ...props } ) {
const { blockType, hasParents } = useSelect( ( select ) => {
const {
getBlockName,
Expand All @@ -43,20 +43,19 @@ function BlockContextualToolbar( { focusOnMount, ...props } ) {
// Shifts the toolbar to make room for the parent block selector.
const classes = classnames( 'block-editor-block-contextual-toolbar', {
'has-parent': hasParents,
'is-fixed': isFixed,
} );

return (
<div className="block-editor-block-contextual-toolbar-wrapper">
<NavigableToolbar
focusOnMount={ focusOnMount }
className={ classes }
/* translators: accessibility text for the block toolbar */
aria-label={ __( 'Block tools' ) }
{ ...props }
>
<BlockToolbar />
</NavigableToolbar>
</div>
<NavigableToolbar
focusOnMount={ focusOnMount }
className={ classes }
/* translators: accessibility text for the block toolbar */
aria-label={ __( 'Block tools' ) }
{ ...props }
>
<BlockToolbar hideDragHandle={ isFixed } />
</NavigableToolbar>
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ function BlockPopover( {
isValid,
isEmptyDefaultBlock,
capturingClientId,
__unstablePopoverSlot,
} ) {
const {
isNavigationMode,
Expand Down Expand Up @@ -180,7 +181,9 @@ function BlockPopover( {
anchorRef={ anchorRef }
className="block-editor-block-list__block-popover"
__unstableStickyBoundaryElement={ stickyBoundaryElement }
__unstableSlotName="block-toolbar"
// Render in the old slot if needed for backward compatibility,
// otherwise render in place (not in the the default popover slot).
__unstableSlotName={ __unstablePopoverSlot || null }
__unstableBoundaryParent
// Observe movement for block animations (especially horizontal).
__unstableObserveElement={ node }
Expand Down Expand Up @@ -293,7 +296,7 @@ function wrapperSelector( select ) {
};
}

export default function WrappedBlockPopover() {
export default function WrappedBlockPopover( { __unstablePopoverSlot } ) {
const selected = useSelect( wrapperSelector, [] );

if ( ! selected ) {
Expand All @@ -320,6 +323,7 @@ export default function WrappedBlockPopover() {
isValid={ isValid }
isEmptyDefaultBlock={ isEmptyDefaultBlock }
capturingClientId={ capturingClientId }
__unstablePopoverSlot={ __unstablePopoverSlot }
/>
);
}
32 changes: 32 additions & 0 deletions packages/block-editor/src/components/block-tools/index.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,46 @@
/**
* WordPress dependencies
*/
import { useSelect } from '@wordpress/data';
import { useViewportMatch } from '@wordpress/compose';
import { Popover } from '@wordpress/components';

/**
* Internal dependencies
*/
import InsertionPoint from './insertion-point';
import BlockPopover from './block-popover';
import { store as blockEditorStore } from '../../store';
import BlockContextualToolbar from './block-contextual-toolbar';

/**
* 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.
*
* @param {Object} $0 Props.
* @param {Object} $0.children The block content and style container.
*/
export default function BlockTools( { children } ) {
const isLargeViewport = useViewportMatch( 'medium' );
const hasFixedToolbar = useSelect(
( select ) => select( blockEditorStore ).getSettings().hasFixedToolbar,
[]
);

return (
<InsertionPoint>
{ ( hasFixedToolbar || ! isLargeViewport ) && (
Copy link
Contributor

@talldan talldan May 6, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In an earlier iteration this component was just implementing the two forms of the block toolbar, and I think it was good to include the viewport logic in that iteration. The UnifiedBlockToolbar component became a convenient and quick way to implement that kind of responsive toolbar.

But now this is bundled with some pretty key functionality for the block editor it seems fairly opinionated to say that someone implementing an editor should have a fixed toolbar on small viewports.

I don't think it's a blocker, this PR still makes things much easier overall. And an implementer can still reimplement this component without the viewport code. But I think it does pose a question about whether BlockEditor should be exporting high-level or low-level components.

Copy link
Member Author

@ellatrix ellatrix May 6, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can have a prop on this component hasFixedToolbar that overrides the setting? That's easy to add. :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No rush to do that right now, just some food for thought. 😄

<BlockContextualToolbar isFixed />
) }
{ /* Even if the toolbar is fixed, the block popover is still
needed for navigation mode. */ }
<BlockPopover />
{ /* Used for the inline rich text toolbar. */ }
<Popover.Slot name="block-toolbar" />
{ children }
{ /* Forward compatibility: a place to render block tools behind the
content so it can be tabbed to properly. */ }
</InsertionPoint>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { __unstableUseBlockElement as useBlockElement } from '../block-list/use-

export const InsertionPointOpenRef = createContext();

function InsertionPointPopover() {
function InsertionPointPopover( { __unstablePopoverSlot } ) {
const { selectBlock } = useDispatch( blockEditorStore );
const openRef = useContext( InsertionPointOpenRef );
const ref = useRef();
Expand Down Expand Up @@ -117,6 +117,7 @@ function InsertionPointPopover() {
}, [ previousElement, nextElement ] );

const getAnchorRect = useCallback( () => {
const { ownerDocument } = previousElement;
const previousRect = previousElement.getBoundingClientRect();
const nextRect = nextElement
? nextElement.getBoundingClientRect()
Expand All @@ -128,6 +129,7 @@ function InsertionPointPopover() {
left: previousRect.right,
right: previousRect.left,
bottom: nextRect ? nextRect.top : previousRect.bottom,
ownerDocument,
};
}

Expand All @@ -136,6 +138,7 @@ function InsertionPointPopover() {
left: previousRect.left,
right: previousRect.right,
bottom: nextRect ? nextRect.top : previousRect.bottom,
ownerDocument,
};
}

Expand All @@ -145,6 +148,7 @@ function InsertionPointPopover() {
left: nextRect ? nextRect.right : previousRect.left,
right: previousRect.left,
bottom: previousRect.bottom,
ownerDocument,
};
}

Expand All @@ -153,6 +157,7 @@ function InsertionPointPopover() {
left: previousRect.right,
right: nextRect ? nextRect.left : previousRect.right,
bottom: previousRect.bottom,
ownerDocument,
};
}, [ previousElement, nextElement ] );

Expand Down Expand Up @@ -205,7 +210,9 @@ function InsertionPointPopover() {
getAnchorRect={ getAnchorRect }
focusOnMount={ false }
className="block-editor-block-list__insertion-point-popover"
__unstableSlotName="block-toolbar"
// Render in the old slot if needed for backward compatibility,
// otherwise render in place (not in the the default popover slot).
__unstableSlotName={ __unstablePopoverSlot || null }
>
<div
ref={ ref }
Expand Down
54 changes: 33 additions & 21 deletions packages/block-editor/src/components/block-tools/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,25 @@
border-right-color: $gray-900;
}

&.is-fixed {
position: sticky;
top: 0;
width: 100%;
z-index: z-index(".block-editor-block-list__block-popover");
// Fill up when empty
min-height: $block-toolbar-height;
display: block;

border: none;
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;
}
}

.block-editor-block-mover-button {
overflow: hidden;
}
Expand All @@ -140,29 +159,23 @@
}

// Position mover arrows for both toolbars.
.block-editor-block-contextual-toolbar,
.edit-post-header-toolbar__block-toolbar,
.edit-site-header-toolbar__block-toolbar,
.edit-navigation-layout__block-toolbar,
.edit-widgets-header__block-toolbar {
.block-editor-block-mover:not(.is-horizontal) {
// Position SVGs.
.block-editor-block-mover-button {
&:focus::before {
left: 0 !important;
min-width: 0;
width: 100%;
}
.block-editor-block-contextual-toolbar .block-editor-block-mover:not(.is-horizontal) {
// Position SVGs.
.block-editor-block-mover-button {
&:focus::before {
left: 0 !important;
min-width: 0;
width: 100%;
}
}

@include break-small() {
.block-editor-block-mover-button.is-up-button svg {
top: 5px;
}
@include break-small() {
.block-editor-block-mover-button.is-up-button svg {
top: 5px;
}

.block-editor-block-mover-button.is-down-button svg {
bottom: 5px;
}
.block-editor-block-mover-button.is-down-button svg {
bottom: 5px;
}
}
}
Expand Down Expand Up @@ -234,7 +247,6 @@
}
}


/**
* Popovers.
*/
Expand Down
1 change: 1 addition & 0 deletions packages/block-editor/src/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ export { default as BlockSettingsMenu } from './block-settings-menu';
export { default as BlockSettingsMenuControls } from './block-settings-menu-controls';
export { default as BlockTitle } from './block-title';
export { default as BlockToolbar } from './block-toolbar';
export { default as BlockTools } from './block-tools';
export {
default as CopyHandler,
useClipboardHandler as __unstableUseClipboardHandler,
Expand Down
Loading