-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
Template Parts & Reusable Blocks - try overlay element for clickthrough to edit pattern. #31109
Changes from all commits
8da3a6a
6cbfb21
89f6cb8
9b685c0
5d363d7
51b0a9f
562a393
e16a091
f27bcb2
6eb82de
e5d4f3f
621dcd9
bcbabf9
0d45917
a3e35e6
a1ae072
7d74900
0550d76
5272c12
8085ce9
fd702eb
5351165
8ff4b94
b760a07
8639351
08ecb26
1d0d27b
cf2b380
00218e0
35d61f4
882fbf8
15157e9
f4b6f43
84c686f
b00bb3f
d52dc45
ff04eb3
57e6a8d
6a6ad3a
2fe7645
5344a22
3114fdf
3a3f398
ba5decc
6055b49
a471f05
bdd3ad3
8f7d8be
2bf4aca
a7ea5e4
58cdcf5
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 |
---|---|---|
@@ -0,0 +1,9 @@ | ||
/** | ||
* Converts a hex value into the rgb equivalent. | ||
* | ||
* @param {string} hex - the hexadecimal value to convert | ||
* @return {string} comma separated rgb values | ||
*/ | ||
@function hex-to-rgb($hex) { | ||
@return red($hex), green($hex), blue($hex); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { useSelect } from '@wordpress/data'; | ||
import { useState, useEffect } from '@wordpress/element'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import { store as blockEditorStore } from '../../store'; | ||
|
||
/** | ||
* External dependencies | ||
*/ | ||
import classnames from 'classnames'; | ||
|
||
export default function BlockContentOverlay( { | ||
clientId, | ||
tagName: TagName = 'div', | ||
wrapperProps, | ||
className, | ||
} ) { | ||
const baseClassName = 'block-editor-block-content-overlay'; | ||
const [ isOverlayActive, setIsOverlayActive ] = useState( true ); | ||
const [ isHovered, setIsHovered ] = useState( false ); | ||
|
||
const { | ||
isParentSelected, | ||
hasChildSelected, | ||
isDraggingBlocks, | ||
isParentHighlighted, | ||
} = useSelect( | ||
( select ) => { | ||
const { | ||
isBlockSelected, | ||
hasSelectedInnerBlock, | ||
isDraggingBlocks: _isDraggingBlocks, | ||
isBlockHighlighted, | ||
} = select( blockEditorStore ); | ||
return { | ||
isParentSelected: isBlockSelected( clientId ), | ||
hasChildSelected: hasSelectedInnerBlock( clientId, true ), | ||
isDraggingBlocks: _isDraggingBlocks(), | ||
isParentHighlighted: isBlockHighlighted( clientId ), | ||
}; | ||
}, | ||
[ clientId ] | ||
); | ||
|
||
const classes = classnames( | ||
baseClassName, | ||
wrapperProps?.className, | ||
className, | ||
{ | ||
'overlay-active': isOverlayActive, | ||
'parent-highlighted': isParentHighlighted, | ||
'is-dragging-blocks': isDraggingBlocks, | ||
} | ||
); | ||
|
||
useEffect( () => { | ||
// Reenable when blocks are not in use. | ||
if ( ! isParentSelected && ! hasChildSelected && ! isOverlayActive ) { | ||
setIsOverlayActive( true ); | ||
} | ||
// Disable if parent selected by another means (such as list view). | ||
// We check hover to ensure the overlay click interaction is not taking place. | ||
// Trying to click the overlay will select the parent block via its 'focusin' | ||
// listener on the wrapper, so if the block is selected while hovered we will | ||
// let the mouseup disable the overlay instead. | ||
if ( isParentSelected && ! isHovered && isOverlayActive ) { | ||
setIsOverlayActive( false ); | ||
} | ||
// Ensure overlay is disabled if a child block is selected. | ||
if ( hasChildSelected && isOverlayActive ) { | ||
setIsOverlayActive( false ); | ||
} | ||
}, [ isParentSelected, hasChildSelected, isOverlayActive, isHovered ] ); | ||
|
||
// Disabled because the overlay div doesn't actually have a role or functionality | ||
// as far as the a11y is concerned. We're just catching the first click so that | ||
// the block can be selected without interacting with its contents. | ||
/* eslint-disable jsx-a11y/no-static-element-interactions */ | ||
return ( | ||
<TagName | ||
{ ...wrapperProps } | ||
className={ classes } | ||
onMouseEnter={ () => setIsHovered( true ) } | ||
onMouseLeave={ () => setIsHovered( false ) } | ||
> | ||
{ isOverlayActive && ( | ||
<div | ||
className={ `${ baseClassName }__overlay` } | ||
onMouseUp={ () => setIsOverlayActive( false ) } | ||
/> | ||
) } | ||
{ wrapperProps?.children } | ||
</TagName> | ||
); | ||
} | ||
/* eslint-enable jsx-a11y/no-static-element-interactions */ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
// Specificity required to ensure overlay width is not restricted to that | ||
// of standard block content. The overlay's width should be as wide as | ||
// its children require. | ||
.editor-styles-wrapper .wp-block .block-editor-block-content-overlay__overlay { | ||
max-width: none; | ||
} | ||
|
||
.block-editor-block-content-overlay { | ||
.block-editor-block-content-overlay__overlay { | ||
position: absolute; | ||
top: 0; | ||
left: 0; | ||
width: 100%; | ||
height: 100%; | ||
background: transparent; | ||
border: none; | ||
border-radius: $radius-block-ui; | ||
z-index: z-index(".block-editor-block-content-overlay__overlay"); | ||
} | ||
|
||
&:hover:not(.is-dragging-blocks), | ||
&.parent-highlighted { | ||
> .block-editor-block-content-overlay__overlay { | ||
background: rgba(var(--wp-admin-theme-color--rgb), 0.1); | ||
box-shadow: 0 0 0 $border-width var(--wp-admin-theme-color) inset; | ||
} | ||
} | ||
|
||
&.overlay-active:not(.is-dragging-blocks) { | ||
*:not(.block-editor-block-content-overlay__overlay) { | ||
pointer-events: none; | ||
} | ||
} | ||
|
||
&.is-dragging-blocks { | ||
box-shadow: 0 0 0 $border-width var(--wp-admin-theme-color); | ||
.block-editor-block-content-overlay__overlay { | ||
pointer-events: none; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,7 @@ import { useEntityBlockEditor } from '@wordpress/core-data'; | |
import { | ||
InnerBlocks, | ||
__experimentalUseInnerBlocksProps as useInnerBlocksProps, | ||
__experimentalBlockContentOverlay as BlockContentOverlay, | ||
useSetting, | ||
store as blockEditorStore, | ||
} from '@wordpress/block-editor'; | ||
|
@@ -15,8 +16,9 @@ export default function TemplatePartInnerBlocks( { | |
postId: id, | ||
hasInnerBlocks, | ||
layout, | ||
tagName: TagName, | ||
tagName, | ||
blockProps, | ||
clientId, | ||
} ) { | ||
const themeSupportsLayout = useSelect( ( select ) => { | ||
const { getSettings } = select( blockEditorStore ); | ||
|
@@ -45,6 +47,7 @@ export default function TemplatePartInnerBlocks( { | |
'wp_template_part', | ||
{ id } | ||
); | ||
|
||
const innerBlocksProps = useInnerBlocksProps( blockProps, { | ||
value: blocks, | ||
onInput, | ||
|
@@ -54,5 +57,12 @@ export default function TemplatePartInnerBlocks( { | |
: InnerBlocks.ButtonBlockAppender, | ||
__experimentalLayout: _layout, | ||
} ); | ||
return <TagName { ...innerBlocksProps } />; | ||
|
||
return ( | ||
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. I think the changes here could potentially break alignments inside template parts. 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. Ive mostly been testing on a header including a nested template part, and the alignments seem to be the same as before and correspond to the front-end output. I definitely wouldn't want to break any alignments though, is there anything specific use case that might make this problematic? 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. I think this new approach should be a bit more safe here. |
||
<BlockContentOverlay | ||
clientId={ clientId } | ||
tagName={ tagName } | ||
wrapperProps={ innerBlocksProps } | ||
/> | ||
); | ||
} |
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.
Is there on way to do this with less elements involved? Maybe pointer-events: none and enable when the block is selected?
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'm pretty sure we did this already in #34012
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.
Nice! Sorry, missed that.