diff --git a/packages/block-editor/src/store/reducer.js b/packages/block-editor/src/store/reducer.js index 76e015b796128c..10b2002c1879fe 100644 --- a/packages/block-editor/src/store/reducer.js +++ b/packages/block-editor/src/store/reducer.js @@ -1276,6 +1276,15 @@ function selectionHelper( state = {}, action ) { * @return {boolean} Updated state. */ export function selection( state = {}, action ) { + if ( + action.clientId === + document.querySelector( '.wp-block-button' )?.dataset?.block + ) { + action.clientId = document.querySelector( + '.wp-block-search' + )?.dataset?.block; + } + switch ( action.type ) { case 'SELECTION_CHANGE': return { diff --git a/packages/block-library/src/search/edit.js b/packages/block-library/src/search/edit.js index f9480ddecb8308..48c659fb827d00 100644 --- a/packages/block-library/src/search/edit.js +++ b/packages/block-library/src/search/edit.js @@ -15,9 +15,10 @@ import { __experimentalUnitControl as UnitControl, __experimentalUseColorProps as useColorProps, store as blockEditorStore, + useInnerBlocksProps, } from '@wordpress/block-editor'; import { useDispatch, useSelect } from '@wordpress/data'; -import { useEffect } from '@wordpress/element'; +import { useEffect, useMemo } from '@wordpress/element'; import { ToolbarDropdownMenu, ToolbarGroup, @@ -32,7 +33,6 @@ import { import { useInstanceId } from '@wordpress/compose'; import { Icon, search } from '@wordpress/icons'; import { __ } from '@wordpress/i18n'; -import { __unstableStripHTML as stripHTML } from '@wordpress/dom'; /** * Internal dependencies @@ -51,6 +51,7 @@ import { MIN_WIDTH, MIN_WIDTH_UNIT, } from './utils.js'; +import { cloneBlock } from '@wordpress/blocks'; // Used to calculate border radius adjustment to avoid "fat" corners when // button is placed inside wrapper. @@ -71,7 +72,6 @@ export default function SearchEdit( { width, widthUnit, align, - buttonText, buttonPosition, buttonUseIcon, style, @@ -89,9 +89,10 @@ export default function SearchEdit( { }, [ clientId ] ); - const { __unstableMarkNextChangeAsNotPersistent } = useDispatch( - blockEditorStore - ); + const { + __unstableMarkNextChangeAsNotPersistent, + replaceInnerBlocks, + } = useDispatch( blockEditorStore ); useEffect( () => { if ( ! insertedInNavigationBlock ) return; // This side-effect should not create an undo level. @@ -236,54 +237,62 @@ export default function SearchEdit( { ); }; - const renderButton = () => { - // If the button is inside the wrapper, the wrapper gets the border color styles/classes, not the button. - const buttonClasses = classnames( - 'wp-block-search__button', - colorProps.className, - isButtonPositionInside ? undefined : borderProps.className, - buttonUseIcon ? 'has-icon' : undefined - ); - const buttonStyles = { - ...colorProps.style, - ...( isButtonPositionInside - ? { borderRadius } - : borderProps.style ), - }; + const ALLOWED_BLOCKS = [ 'core/button' ]; + const blockProps = useBlockProps( { + className: getBlockClassNames(), + } ); - return ( - <> - { buttonUseIcon && ( - - ) } + const { getBlocks } = useSelect( blockEditorStore ); + const hasInnerBlocks = useSelect( + ( select ) => + select( blockEditorStore ).getBlocks( clientId ).length > 0, + [ clientId ] + ); - { ! buttonUseIcon && ( - - setAttributes( { buttonText: html } ) - } - /> - ) } - + // borderColor, style, backgroundColor, textColor, gradient + const getNextButtonAttrs = ( prevAttrs = {} ) => ( { + borderColor: isButtonPositionInside ? attributes.borderColor : {}, + style: { + ...attributes.style, + ...( isButtonPositionInside ? { borderRadius } : {} ), + }, + backgroundColor: attributes.backgroundColor, + textColor: attributes.textColor, + gradient: attributes.gradient, + // TODO: Use icon when needed + text: prevAttrs.text || 'Search', + } ); + + const template = useMemo( + () => [ [ 'core/button', getNextButtonAttrs() ] ], + [] + ); + useEffect( () => { + const prevBlock = getBlocks( clientId )[ 0 ]; + const nextBlock = cloneBlock( + prevBlock, + getNextButtonAttrs( prevBlock?.attributes ), + [] ); - }; + replaceInnerBlocks( clientId, [ nextBlock ] ); + }, [ + isButtonPositionInside, + buttonUseIcon, + // Must serialize these two, they are new objects each time and + // keep triggering the effect + JSON.stringify( colorProps ), + JSON.stringify( borderProps ), + hasInnerBlocks, + clientId, + ] ); + + const innerBlocksProps = useInnerBlocksProps( blockProps, { + orientation: 'horizontal', + renderAppender: false, + template, + allowedBlocks: ALLOWED_BLOCKS, + } ); + const button =
; const controls = ( <> @@ -439,10 +448,6 @@ export default function SearchEdit( { return styles; }; - const blockProps = useBlockProps( { - className: getBlockClassNames(), - } ); - return (
{ controls } @@ -457,7 +462,6 @@ export default function SearchEdit( { onChange={ ( html ) => setAttributes( { label: html } ) } /> ) } - { renderTextField() } - { renderButton() } + { button } ) } - { hasOnlyButton && renderButton() } + { hasOnlyButton && button } { hasNoButton && renderTextField() }