-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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
Render Selected Block Tools in Header when using Top Toolbar #55787
Changes from all commits
2c813ae
45e1888
8b0cd57
0d580ba
c31e186
d634599
7c96b45
1b3186e
b7348ae
ea00096
1c1cd73
62a4591
27432cc
dd90378
238f4e5
5e57aee
8c9dd02
dd7cade
558ff4a
4941d98
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -56,6 +56,14 @@ | |
} | ||
} | ||
|
||
.block-editor-block-contextual-toolbar.is-fixed { | ||
position: sticky; | ||
top: 0; | ||
z-index: z-index(".block-editor-block-popover"); | ||
display: block; | ||
width: 100%; | ||
} | ||
Comment on lines
+59
to
+65
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Styles for small screen fixed toolbar. This was previously spread over the edit-post, edit-site, and edit-widgets packages, but looks like a core style that is applicable for the standalone editor design. |
||
|
||
// on desktop browsers the fixed toolbar has tweaked borders | ||
@include break-medium() { | ||
.block-editor-block-contextual-toolbar.is-fixed { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,22 +7,8 @@ import classnames from 'classnames'; | |
* WordPress dependencies | ||
*/ | ||
import { __ } from '@wordpress/i18n'; | ||
import { | ||
forwardRef, | ||
useLayoutEffect, | ||
useEffect, | ||
useRef, | ||
useState, | ||
} from '@wordpress/element'; | ||
import { hasBlockSupport, store as blocksStore } from '@wordpress/blocks'; | ||
import { useSelect } from '@wordpress/data'; | ||
import { | ||
ToolbarItem, | ||
ToolbarButton, | ||
ToolbarGroup, | ||
} from '@wordpress/components'; | ||
import { next, previous } from '@wordpress/icons'; | ||
import { useViewportMatch } from '@wordpress/compose'; | ||
|
||
/** | ||
* Internal dependencies | ||
|
@@ -32,141 +18,46 @@ import BlockToolbar from '../block-toolbar'; | |
import { store as blockEditorStore } from '../../store'; | ||
import { useHasAnyBlockControls } from '../block-controls/use-has-block-controls'; | ||
|
||
function UnforwardedBlockContextualToolbar( | ||
{ focusOnMount, isFixed, ...props }, | ||
ref | ||
) { | ||
// When the toolbar is fixed it can be collapsed | ||
const [ isCollapsed, setIsCollapsed ] = useState( false ); | ||
const toolbarButtonRef = useRef(); | ||
|
||
const isLargeViewport = useViewportMatch( 'medium' ); | ||
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', | ||
}; | ||
}, [] ); | ||
|
||
useEffect( () => { | ||
setIsCollapsed( false ); | ||
}, [ selectedBlockClientId ] ); | ||
|
||
const isLargerThanTabletViewport = useViewportMatch( 'large', '>=' ); | ||
const isFullscreen = | ||
document.body.classList.contains( 'is-fullscreen-mode' ); | ||
|
||
/** | ||
* The following code is a workaround to fix the width of the toolbar | ||
* it should be removed when the toolbar will be rendered inline | ||
* FIXME: remove this layout effect when the toolbar is no longer | ||
* absolutely positioned | ||
*/ | ||
useLayoutEffect( () => { | ||
// don't do anything if not fixed toolbar | ||
if ( ! isFixed ) { | ||
return; | ||
} | ||
|
||
const blockToolbar = document.querySelector( | ||
'.block-editor-block-contextual-toolbar' | ||
); | ||
|
||
if ( ! blockToolbar ) { | ||
return; | ||
} | ||
|
||
if ( ! blockType ) { | ||
blockToolbar.style.width = 'initial'; | ||
return; | ||
} | ||
|
||
if ( ! isLargerThanTabletViewport ) { | ||
// set the width of the toolbar to auto | ||
blockToolbar.style = {}; | ||
return; | ||
} | ||
|
||
if ( isCollapsed ) { | ||
// set the width of the toolbar to auto | ||
blockToolbar.style.width = 'auto'; | ||
return; | ||
} | ||
|
||
// get the width of the pinned items in the post editor or widget editor | ||
const pinnedItems = document.querySelector( | ||
'.edit-post-header__settings, .edit-widgets-header__actions' | ||
); | ||
// get the width of the left header in the site editor | ||
const leftHeader = document.querySelector( | ||
'.edit-site-header-edit-mode__end' | ||
); | ||
|
||
const computedToolbarStyle = window.getComputedStyle( blockToolbar ); | ||
const computedPinnedItemsStyle = pinnedItems | ||
? window.getComputedStyle( pinnedItems ) | ||
: false; | ||
const computedLeftHeaderStyle = leftHeader | ||
? window.getComputedStyle( leftHeader ) | ||
: false; | ||
|
||
const marginLeft = parseFloat( computedToolbarStyle.marginLeft ); | ||
const pinnedItemsWidth = computedPinnedItemsStyle | ||
? parseFloat( computedPinnedItemsStyle.width ) | ||
: 0; | ||
const leftHeaderWidth = computedLeftHeaderStyle | ||
? parseFloat( computedLeftHeaderStyle.width ) | ||
: 0; | ||
|
||
// set the new witdth of the toolbar | ||
blockToolbar.style.width = `calc(100% - ${ | ||
leftHeaderWidth + | ||
pinnedItemsWidth + | ||
marginLeft + | ||
( pinnedItems || leftHeader ? 2 : 0 ) + // Prevents button focus border from being cut off | ||
( isFullscreen ? 0 : 160 ) // the width of the admin sidebar expanded | ||
}px)`; | ||
}, [ | ||
isFixed, | ||
isLargerThanTabletViewport, | ||
isCollapsed, | ||
isFullscreen, | ||
blockType, | ||
] ); | ||
export default function BlockContextualToolbar( { | ||
focusOnMount, | ||
isFixed, | ||
...props | ||
} ) { | ||
const { blockType, blockEditingMode, hasParents, showParentSelector } = | ||
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 && | ||
|
@@ -183,12 +74,10 @@ function UnforwardedBlockContextualToolbar( | |
const classes = classnames( 'block-editor-block-contextual-toolbar', { | ||
'has-parent': hasParents && showParentSelector, | ||
'is-fixed': isFixed, | ||
'is-collapsed': isCollapsed, | ||
} ); | ||
|
||
return ( | ||
<NavigableToolbar | ||
ref={ ref } | ||
focusOnMount={ focusOnMount } | ||
focusEditorOnEscape | ||
className={ classes } | ||
|
@@ -197,37 +86,7 @@ function UnforwardedBlockContextualToolbar( | |
variant={ isFixed ? 'unstyled' : undefined } | ||
{ ...props } | ||
> | ||
{ ! isCollapsed && <BlockToolbar hideDragHandle={ isFixed } /> } | ||
{ isFixed && isLargeViewport && blockType && ( | ||
<ToolbarGroup | ||
className={ | ||
isCollapsed | ||
? 'block-editor-block-toolbar__group-expand-fixed-toolbar' | ||
: 'block-editor-block-toolbar__group-collapse-fixed-toolbar' | ||
} | ||
> | ||
<ToolbarItem | ||
as={ ToolbarButton } | ||
ref={ toolbarButtonRef } | ||
icon={ isCollapsed ? next : previous } | ||
onClick={ () => { | ||
setIsCollapsed( ( collapsed ) => ! collapsed ); | ||
toolbarButtonRef.current.focus(); | ||
} } | ||
label={ | ||
isCollapsed | ||
? __( 'Show block tools' ) | ||
: __( 'Hide block tools' ) | ||
} | ||
/> | ||
</ToolbarGroup> | ||
) } | ||
<BlockToolbar hideDragHandle={ isFixed } /> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The Collapsed button is now a part of the Site Editor header, as that was the only place it was used. Also, the block toolbar does not need to know about the Collapsed state. The rest of the red in this file is related to removing the absolute positioning hacks. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The component here is named
I know this has changed over time but this PR might be the opportunity to restore that simple behavior. |
||
</NavigableToolbar> | ||
); | ||
} | ||
|
||
export const BlockContextualToolbar = forwardRef( | ||
UnforwardedBlockContextualToolbar | ||
); | ||
|
||
export default BlockContextualToolbar; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know it hasn't been introduce in this PR but the isFixed prop in the contextual toolbar is weird. It's "contextual" toolbar why have an isFixed prop :). Anyway, I'm curious when this was introduced and the reasoning for it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know the reason why it was added, but my interpretation of contextual meant "tools filled contextually with relevant block actions." As in, the block tools were all contextual relevant to the block selected. I did not interpret "contextual" as "adjacent to." I think changing the component name to have Popover in it would be clearer, like
<BlockToolbarPopover />
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We already have things like
BlockPopover
andSelectedBlockPopover
which is probably very similar to what you propose. It seems we do need to bring some clarity to the components and more importantly have a clear purpose for each component that stays clear as we iterate and refactor.