From f13bd138bcfdb8ac12aa0f6007850f5c8c0e2108 Mon Sep 17 00:00:00 2001 From: Ella Date: Thu, 7 Dec 2023 13:40:46 +0200 Subject: [PATCH 1/8] One hook to rule them all: preparation for a block supports API --- packages/block-editor/src/hooks/align.js | 42 ++-------------- packages/block-editor/src/hooks/anchor.js | 43 ++-------------- packages/block-editor/src/hooks/duotone.js | 57 +++------------------- packages/block-editor/src/hooks/index.js | 57 ++++++++++++++++++++-- packages/block-editor/src/hooks/layout.js | 46 +++-------------- 5 files changed, 80 insertions(+), 165 deletions(-) diff --git a/packages/block-editor/src/hooks/align.js b/packages/block-editor/src/hooks/align.js index 3b916d9577f1a7..5cd4e6ca940a2e 100644 --- a/packages/block-editor/src/hooks/align.js +++ b/packages/block-editor/src/hooks/align.js @@ -159,38 +159,11 @@ const BlockEditAlignmentToolbarControls = pure( BlockEditAlignmentToolbarControlsPure ); -/** - * Override the default edit UI to include new toolbar controls for block - * alignment, if block defines support. - * - * @param {Function} BlockEdit Original component. - * - * @return {Function} Wrapped component. - */ -export const withAlignmentControls = createHigherOrderComponent( - ( BlockEdit ) => ( props ) => { - const hasAlignmentSupport = hasBlockSupport( - props.name, - 'align', - false - ); - - return ( - <> - { hasAlignmentSupport && ( - - ) } - - - ); - }, - 'withAlignmentControls' -); +export const BlockEdit = BlockEditAlignmentToolbarControls; +export const attributeKeys = [ 'align' ]; +export function hasSupport( blockName ) { + return hasBlockSupport( blockName, 'align', false ); +} function BlockListBlockWithDataAlign( { block: BlockListBlock, props } ) { const { name, attributes } = props; @@ -273,11 +246,6 @@ addFilter( 'core/editor/align/with-data-align', withDataAlign ); -addFilter( - 'editor.BlockEdit', - 'core/editor/align/with-toolbar-controls', - withAlignmentControls -); addFilter( 'blocks.getSaveContent.extraProps', 'core/editor/align/addAssignedAlign', diff --git a/packages/block-editor/src/hooks/anchor.js b/packages/block-editor/src/hooks/anchor.js index 9902ed479531c6..dce1aff46b1506 100644 --- a/packages/block-editor/src/hooks/anchor.js +++ b/packages/block-editor/src/hooks/anchor.js @@ -5,7 +5,6 @@ import { addFilter } from '@wordpress/hooks'; import { PanelBody, TextControl, ExternalLink } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; import { hasBlockSupport } from '@wordpress/blocks'; -import { createHigherOrderComponent, pure } from '@wordpress/compose'; import { Platform } from '@wordpress/element'; /** @@ -116,38 +115,11 @@ function BlockEditAnchorControlPure( { blockName, anchor, setAttributes } ) { ); } -// We don't want block controls to re-render when typing inside a block. `pure` -// will prevent re-renders unless props change, so only pass the needed props -// and not the whole attributes object. -const BlockEditAnchorControl = pure( BlockEditAnchorControlPure ); - -/** - * Override the default edit UI to include a new block inspector control for - * assigning the anchor ID, if block supports anchor. - * - * @param {Component} BlockEdit Original component. - * - * @return {Component} Wrapped component. - */ -export const withAnchorControls = createHigherOrderComponent( ( BlockEdit ) => { - return ( props ) => { - return ( - <> - - { props.isSelected && - hasBlockSupport( props.name, 'anchor' ) && ( - - ) } - - ); - }; -}, 'withAnchorControls' ); +export const BlockEdit = BlockEditAnchorControlPure; +export const attributeKeys = [ 'anchor' ]; +export function hasSupport( name ) { + return hasBlockSupport( name, 'anchor' ); +} /** * Override props assigned to save component to inject anchor ID, if block @@ -169,11 +141,6 @@ export function addSaveProps( extraProps, blockType, attributes ) { } addFilter( 'blocks.registerBlockType', 'core/anchor/attribute', addAttribute ); -addFilter( - 'editor.BlockEdit', - 'core/editor/anchor/with-inspector-controls', - withAnchorControls -); addFilter( 'blocks.getSaveContent.extraProps', 'core/editor/anchor/save-props', diff --git a/packages/block-editor/src/hooks/duotone.js b/packages/block-editor/src/hooks/duotone.js index 6e18b44cef1633..c5efb0466b09c6 100644 --- a/packages/block-editor/src/hooks/duotone.js +++ b/packages/block-editor/src/hooks/duotone.js @@ -13,11 +13,7 @@ import { getBlockType, hasBlockSupport, } from '@wordpress/blocks'; -import { - createHigherOrderComponent, - useInstanceId, - pure, -} from '@wordpress/compose'; +import { createHigherOrderComponent, useInstanceId } from '@wordpress/compose'; import { addFilter } from '@wordpress/hooks'; import { useMemo, useEffect } from '@wordpress/element'; @@ -179,10 +175,16 @@ function DuotonePanelPure( { style, setAttributes, name } ) { ); } +export const attributeKeys = [ 'style' ]; + +export function hasSupport( name ) { + return hasBlockSupport( name, 'filter.duotone' ); +} + // We don't want block controls to re-render when typing inside a block. `pure` // will prevent re-renders unless props change, so only pass the needed props // and not the whole attributes object. -const DuotonePanel = pure( DuotonePanelPure ); +export const BlockEdit = DuotonePanelPure; /** * Filters registered block settings, extending attributes to include @@ -212,44 +214,6 @@ function addDuotoneAttributes( settings ) { return settings; } -/** - * Override the default edit UI to include toolbar controls for duotone if the - * block supports duotone. - * - * @param {Function} BlockEdit Original component. - * - * @return {Function} Wrapped component. - */ -const withDuotoneControls = createHigherOrderComponent( - ( BlockEdit ) => ( props ) => { - // Previous `color.__experimentalDuotone` support flag is migrated via - // block_type_metadata_settings filter in `lib/block-supports/duotone.php`. - const hasDuotoneSupport = hasBlockSupport( - props.name, - 'filter.duotone' - ); - - // CAUTION: code added before this line will be executed - // for all blocks, not just those that support duotone. Code added - // above this line should be carefully evaluated for its impact on - // performance. - return ( - <> - { hasDuotoneSupport && ( - - ) } - - - ); - }, - 'withDuotoneControls' -); - function DuotoneStyles( { clientId, id: filterId, @@ -438,11 +402,6 @@ addFilter( 'core/editor/duotone/add-attributes', addDuotoneAttributes ); -addFilter( - 'editor.BlockEdit', - 'core/editor/duotone/with-editor-controls', - withDuotoneControls -); addFilter( 'editor.BlockListBlock', 'core/editor/duotone/with-styles', diff --git a/packages/block-editor/src/hooks/index.js b/packages/block-editor/src/hooks/index.js index c088216c0645cb..d53bde6556fda9 100644 --- a/packages/block-editor/src/hooks/index.js +++ b/packages/block-editor/src/hooks/index.js @@ -1,8 +1,14 @@ +/** + * WordPress dependencies + */ +import { createHigherOrderComponent, pure } from '@wordpress/compose'; +import { addFilter } from '@wordpress/hooks'; + /** * Internal dependencies */ +import useDisplayBlockControls from '../components/use-display-block-controls'; import './compat'; -import './align'; import './lock'; import './anchor'; import './aria-label'; @@ -11,18 +17,27 @@ import './generated-class-name'; import './style'; import './settings'; import './color'; -import './duotone'; import './font-family'; import './font-size'; import './border'; import './position'; -import './layout'; import './content-lock-ui'; import './metadata'; import './custom-fields'; import './block-hooks'; import './block-renaming'; +const features = [ 'layout', 'duotone', 'align', 'anchor' ].map( + ( feature ) => { + const settings = require( `./${ feature }` ); + return { + ...settings, + name: feature, + BlockEdit: pure( settings.BlockEdit ), + }; + } +); + export { useCustomSides } from './dimensions'; export { useLayoutClasses, useLayoutStyles } from './layout'; export { getBorderClassesAndStyles, useBorderProps } from './use-border-props'; @@ -31,3 +46,39 @@ export { getSpacingClassesAndStyles } from './use-spacing-props'; export { getTypographyClassesAndStyles } from './use-typography-props'; export { getGapCSSValue } from './gap'; export { useCachedTruthy } from './use-cached-truthy'; + +export const withBlockEditHooks = createHigherOrderComponent( + ( OriginalBlockEdit ) => ( props ) => { + const shouldDisplayControls = useDisplayBlockControls(); + return [ + ...features.map( + ( { name, BlockEdit, hasSupport, attributeKeys } ) => { + if ( + ! shouldDisplayControls || + ! hasSupport( props.name ) + ) { + return null; + } + const neededProps = {}; + for ( const key of attributeKeys ) { + if ( props.attributes[ key ] ) { + neededProps[ key ] = props.attributes[ key ]; + } + } + return ( + + ); + } + ), + , + ]; + }, + 'withLayoutControls' +); + +addFilter( 'editor.BlockEdit', 'core/editor/hooks', withBlockEditHooks ); diff --git a/packages/block-editor/src/hooks/layout.js b/packages/block-editor/src/hooks/layout.js index 3ea5c56da8e776..cdc368728df1a3 100644 --- a/packages/block-editor/src/hooks/layout.js +++ b/packages/block-editor/src/hooks/layout.js @@ -6,11 +6,7 @@ import classnames from 'classnames'; /** * WordPress dependencies */ -import { - createHigherOrderComponent, - pure, - useInstanceId, -} from '@wordpress/compose'; +import { createHigherOrderComponent, useInstanceId } from '@wordpress/compose'; import { addFilter } from '@wordpress/hooks'; import { getBlockSupport, hasBlockSupport } from '@wordpress/blocks'; import { useSelect } from '@wordpress/data'; @@ -290,10 +286,16 @@ function LayoutPanelPure( { layout, setAttributes, name: blockName } ) { ); } +export const attributeKeys = [ 'layout' ]; + +export function hasSupport( name ) { + return hasLayoutBlockSupport( name, layoutBlockSupportKey ); +} + // We don't want block controls to re-render when typing inside a block. `pure` // will prevent re-renders unless props change, so only pass the needed props // and not the whole attributes object. -const LayoutPanel = pure( LayoutPanelPure ); +export const BlockEdit = LayoutPanelPure; function LayoutTypeSwitcher( { type, onChange } ) { return ( @@ -336,33 +338,6 @@ export function addAttribute( settings ) { return settings; } -/** - * Override the default edit UI to include layout controls - * - * @param {Function} BlockEdit Original component. - * - * @return {Function} Wrapped component. - */ -export const withLayoutControls = createHigherOrderComponent( - ( BlockEdit ) => ( props ) => { - const supportLayout = hasLayoutBlockSupport( props.name ); - - return [ - supportLayout && ( - - ), - , - ]; - }, - 'withLayoutControls' -); - function BlockWithLayoutStyles( { block: BlockListBlock, props } ) { const { name, attributes } = props; const id = useInstanceId( BlockListBlock ); @@ -516,8 +491,3 @@ addFilter( 'core/editor/layout/with-child-layout-styles', withChildLayoutStyles ); -addFilter( - 'editor.BlockEdit', - 'core/editor/layout/with-inspector-controls', - withLayoutControls -); From 35242d79076429b6a7c16b1f27558c94004a0155 Mon Sep 17 00:00:00 2001 From: Ella Date: Thu, 7 Dec 2023 14:10:59 +0200 Subject: [PATCH 2/8] more --- .../block-editor/src/hooks/block-hooks.js | 35 ++--------- .../block-editor/src/hooks/block-renaming.js | 45 ++------------ .../src/hooks/custom-class-name.js | 51 ++-------------- .../block-editor/src/hooks/custom-fields.js | 58 +++---------------- packages/block-editor/src/hooks/index.js | 21 +++++-- packages/block-editor/src/hooks/position.js | 51 ++++------------ 6 files changed, 51 insertions(+), 210 deletions(-) diff --git a/packages/block-editor/src/hooks/block-hooks.js b/packages/block-editor/src/hooks/block-hooks.js index 59c0e3c85486f0..4647f0f74e8a45 100644 --- a/packages/block-editor/src/hooks/block-hooks.js +++ b/packages/block-editor/src/hooks/block-hooks.js @@ -2,14 +2,12 @@ * WordPress dependencies */ import { __ } from '@wordpress/i18n'; -import { addFilter } from '@wordpress/hooks'; import { Fragment, useMemo } from '@wordpress/element'; import { __experimentalHStack as HStack, PanelBody, ToggleControl, } from '@wordpress/components'; -import { createHigherOrderComponent, pure } from '@wordpress/compose'; import { createBlock, store as blocksStore } from '@wordpress/blocks'; import { useDispatch, useSelect } from '@wordpress/data'; @@ -235,32 +233,7 @@ function BlockHooksControlPure( props ) { ); } -// We don't want block controls to re-render when typing inside a block. `pure` -// will prevent re-renders unless props change, so only pass the needed props -// and not the whole attributes object. -const BlockHooksControl = pure( BlockHooksControlPure ); - -export const withBlockHooksControls = createHigherOrderComponent( - ( BlockEdit ) => { - return ( props ) => { - return ( - <> - - { props.isSelected && ( - - ) } - - ); - }; - }, - 'withBlockHooksControls' -); - -addFilter( - 'editor.BlockEdit', - 'core/editor/block-hooks/with-inspector-controls', - withBlockHooksControls -); +export const BlockEdit = BlockHooksControlPure; +export function hasSupport() { + return true; +} diff --git a/packages/block-editor/src/hooks/block-renaming.js b/packages/block-editor/src/hooks/block-renaming.js index 452be6e686dbf4..c48e835a0cb937 100644 --- a/packages/block-editor/src/hooks/block-renaming.js +++ b/packages/block-editor/src/hooks/block-renaming.js @@ -3,7 +3,6 @@ */ import { addFilter } from '@wordpress/hooks'; import { hasBlockSupport } from '@wordpress/blocks'; -import { createHigherOrderComponent, pure } from '@wordpress/compose'; import { __ } from '@wordpress/i18n'; import { TextControl } from '@wordpress/components'; @@ -11,7 +10,6 @@ import { TextControl } from '@wordpress/components'; * Internal dependencies */ import { InspectorControls } from '../components'; -import { useBlockRename } from '../components/block-rename'; /** * Filters registered block settings, adding an `__experimentalLabel` callback if one does not already exist. @@ -47,13 +45,7 @@ export function addLabelCallback( settings ) { return settings; } -function BlockRenameControlPure( { name, metadata, setAttributes } ) { - const { canRename } = useBlockRename( name ); - - if ( ! canRename ) { - return null; - } - +function BlockRenameControlPure( { metadata, setAttributes } ) { return ( ( props ) => { - const { name, attributes, setAttributes, isSelected } = props; - return ( - <> - { isSelected && ( - - ) } - - - ); - }, - 'withToolbarControls' -); - -addFilter( - 'editor.BlockEdit', - 'core/block-rename-ui/with-block-rename-control', - withBlockRenameControl -); +export const BlockEdit = BlockRenameControlPure; +export const attributeKeys = [ 'metadata' ]; +export function hasSupport( name ) { + return hasBlockSupport( name, 'renaming', true ); +} addFilter( 'blocks.registerBlockType', diff --git a/packages/block-editor/src/hooks/custom-class-name.js b/packages/block-editor/src/hooks/custom-class-name.js index 8c0f58ddda682d..070b75da84277d 100644 --- a/packages/block-editor/src/hooks/custom-class-name.js +++ b/packages/block-editor/src/hooks/custom-class-name.js @@ -10,7 +10,6 @@ import { addFilter } from '@wordpress/hooks'; import { TextControl } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; import { hasBlockSupport } from '@wordpress/blocks'; -import { createHigherOrderComponent, pure } from '@wordpress/compose'; /** * Internal dependencies @@ -64,46 +63,11 @@ function CustomClassNameControlsPure( { className, setAttributes } ) { ); } -// We don't want block controls to re-render when typing inside a block. `pure` -// will prevent re-renders unless props change, so only pass the needed props -// and not the whole attributes object. -const CustomClassNameControls = pure( CustomClassNameControlsPure ); - -/** - * Override the default edit UI to include a new block inspector control for - * assigning the custom class name, if block supports custom class name. - * The control is displayed within the Advanced panel in the block inspector. - * - * @param {Component} BlockEdit Original component. - * - * @return {Component} Wrapped component. - */ -export const withCustomClassNameControls = createHigherOrderComponent( - ( BlockEdit ) => { - return ( props ) => { - const hasCustomClassName = hasBlockSupport( - props.name, - 'customClassName', - true - ); - - return ( - <> - - { hasCustomClassName && props.isSelected && ( - - ) } - - ); - }; - }, - 'withCustomClassNameControls' -); +export const BlockEdit = CustomClassNameControlsPure; +export const attributeKeys = [ 'className' ]; +export function hasSupport( name ) { + return hasBlockSupport( name, 'customClassName', true ); +} /** * Override props assigned to save component to inject the className, if block @@ -174,11 +138,6 @@ addFilter( 'core/editor/custom-class-name/attribute', addAttribute ); -addFilter( - 'editor.BlockEdit', - 'core/editor/custom-class-name/with-inspector-controls', - withCustomClassNameControls -); addFilter( 'blocks.getSaveContent.extraProps', 'core/editor/custom-class-name/save-props', diff --git a/packages/block-editor/src/hooks/custom-fields.js b/packages/block-editor/src/hooks/custom-fields.js index 19729d00ad61a6..c7e68507ffffc2 100644 --- a/packages/block-editor/src/hooks/custom-fields.js +++ b/packages/block-editor/src/hooks/custom-fields.js @@ -5,7 +5,6 @@ import { addFilter } from '@wordpress/hooks'; import { PanelBody, TextControl } from '@wordpress/components'; import { __, sprintf } from '@wordpress/i18n'; import { hasBlockSupport } from '@wordpress/blocks'; -import { createHigherOrderComponent, pure } from '@wordpress/compose'; /** * Internal dependencies @@ -91,50 +90,16 @@ function CustomFieldsControlPure( { name, connections, setAttributes } ) { ); } -// We don't want block controls to re-render when typing inside a block. `pure` -// will prevent re-renders unless props change, so only pass the needed props -// and not the whole attributes object. -const CustomFieldsControl = pure( CustomFieldsControlPure ); - -/** - * Override the default edit UI to include a new block inspector control for - * assigning a connection to blocks that has support for connections. - * Currently, only the `core/paragraph` block is supported and there is only a relation - * between paragraph content and a custom field. - * - * @param {Component} BlockEdit Original component. - * - * @return {Component} Wrapped component. - */ -const withCustomFieldsControls = createHigherOrderComponent( ( BlockEdit ) => { - return ( props ) => { - const hasCustomFieldsSupport = hasBlockSupport( - props.name, - '__experimentalConnections', - false - ); - +export const BlockEdit = CustomFieldsControlPure; +export const attributeKeys = [ 'connections' ]; +export function hasSupport( name ) { + return ( + hasBlockSupport( name, '__experimentalConnections', false ) && // Check if the current block is a paragraph or image block. // Currently, only these two blocks are supported. - if ( ! [ 'core/paragraph', 'core/image' ].includes( props.name ) ) { - return ; - } - - return ( - <> - - { hasCustomFieldsSupport && props.isSelected && ( - - ) } - - ); - }; -}, 'withCustomFieldsControls' ); + [ 'core/paragraph', 'core/image' ].includes( name ) + ); +} if ( window.__experimentalConnections || @@ -146,10 +111,3 @@ if ( addAttribute ); } -if ( window.__experimentalConnections ) { - addFilter( - 'editor.BlockEdit', - 'core/editor/connections/with-inspector-controls', - withCustomFieldsControls - ); -} diff --git a/packages/block-editor/src/hooks/index.js b/packages/block-editor/src/hooks/index.js index d53bde6556fda9..f77ab3319e1c96 100644 --- a/packages/block-editor/src/hooks/index.js +++ b/packages/block-editor/src/hooks/index.js @@ -27,16 +27,26 @@ import './custom-fields'; import './block-hooks'; import './block-renaming'; -const features = [ 'layout', 'duotone', 'align', 'anchor' ].map( - ( feature ) => { +const features = [ + 'layout', + 'duotone', + 'align', + 'anchor', + 'block-hooks', + 'block-renaming', + 'custom-class-name', + window.__experimentalConnections ? 'custom-fields' : null, + 'position', +] + .filter( Boolean ) + .map( ( feature ) => { const settings = require( `./${ feature }` ); return { ...settings, name: feature, BlockEdit: pure( settings.BlockEdit ), }; - } -); + } ); export { useCustomSides } from './dimensions'; export { useLayoutClasses, useLayoutStyles } from './layout'; @@ -69,6 +79,7 @@ export const withBlockEditHooks = createHigherOrderComponent( @@ -78,7 +89,7 @@ export const withBlockEditHooks = createHigherOrderComponent( , ]; }, - 'withLayoutControls' + 'withBlockEditHooks' ); addFilter( 'editor.BlockEdit', 'core/editor/hooks', withBlockEditHooks ); diff --git a/packages/block-editor/src/hooks/position.js b/packages/block-editor/src/hooks/position.js index 32d4f6582969ee..16676497dd8ad8 100644 --- a/packages/block-editor/src/hooks/position.js +++ b/packages/block-editor/src/hooks/position.js @@ -318,45 +318,23 @@ export function PositionPanelPure( { } ); } +export const BlockEdit = ( props ) => { + const isPositionDisabled = useIsPositionDisabled( props ); + if ( isPositionDisabled ) { + return; + } + return ; +}; +export const attributeKeys = [ 'style' ]; +export function hasSupport( name ) { + return hasBlockSupport( name, POSITION_SUPPORT_KEY ); +} + // We don't want block controls to re-render when typing inside a block. `pure` // will prevent re-renders unless props change, so only pass the needed props // and not the whole attributes object. const PositionPanel = pure( PositionPanelPure ); -/** - * Override the default edit UI to include position controls. - * - * @param {Function} BlockEdit Original component. - * - * @return {Function} Wrapped component. - */ -export const withPositionControls = createHigherOrderComponent( - ( BlockEdit ) => ( props ) => { - const { name: blockName } = props; - const positionSupport = hasBlockSupport( - blockName, - POSITION_SUPPORT_KEY - ); - const isPositionDisabled = useIsPositionDisabled( props ); - const showPositionControls = positionSupport && ! isPositionDisabled; - - return [ - showPositionControls && ( - - ), - , - ]; - }, - 'withPositionControls' -); - /** * Override the default block element to add the position styles. * @@ -411,8 +389,3 @@ addFilter( 'core/editor/position/with-position-styles', withPositionStyles ); -addFilter( - 'editor.BlockEdit', - 'core/editor/position/with-inspector-controls', - withPositionControls -); From 575fac5e7b901fc40262a739acbb35d4c458b288 Mon Sep 17 00:00:00 2001 From: Ella Date: Thu, 7 Dec 2023 14:17:49 +0200 Subject: [PATCH 3/8] more --- packages/block-editor/src/hooks/index.js | 6 +++- packages/block-editor/src/hooks/style.js | 43 +++++------------------- 2 files changed, 13 insertions(+), 36 deletions(-) diff --git a/packages/block-editor/src/hooks/index.js b/packages/block-editor/src/hooks/index.js index f77ab3319e1c96..a7dc653174f22d 100644 --- a/packages/block-editor/src/hooks/index.js +++ b/packages/block-editor/src/hooks/index.js @@ -37,6 +37,7 @@ const features = [ 'custom-class-name', window.__experimentalConnections ? 'custom-fields' : null, 'position', + 'style', ] .filter( Boolean ) .map( ( feature ) => { @@ -62,7 +63,7 @@ export const withBlockEditHooks = createHigherOrderComponent( const shouldDisplayControls = useDisplayBlockControls(); return [ ...features.map( - ( { name, BlockEdit, hasSupport, attributeKeys } ) => { + ( { name, BlockEdit, hasSupport, attributeKeys = [] } ) => { if ( ! shouldDisplayControls || ! hasSupport( props.name ) @@ -81,6 +82,9 @@ export const withBlockEditHooks = createHigherOrderComponent( name={ props.name } clientId={ props.clientId } setAttributes={ props.setAttributes } + __unstableParentLayout={ + props.__unstableParentLayout + } { ...neededProps } /> ); diff --git a/packages/block-editor/src/hooks/style.js b/packages/block-editor/src/hooks/style.js index 1acb2cda3ac017..d9ad95b3e15684 100644 --- a/packages/block-editor/src/hooks/style.js +++ b/packages/block-editor/src/hooks/style.js @@ -32,7 +32,6 @@ import { SPACING_SUPPORT_KEY, DimensionsPanel, } from './dimensions'; -import useDisplayBlockControls from '../components/use-display-block-controls'; import { shouldSkipSerialization, useStyleOverride, @@ -356,12 +355,16 @@ function BlockStyleControls( { __unstableParentLayout, } ) { const settings = useBlockSettings( name, __unstableParentLayout ); + const blockEditingMode = useBlockEditingMode(); const passedProps = { clientId, name, setAttributes, settings, }; + if ( blockEditingMode !== 'default' ) { + return null; + } return ( <> @@ -373,34 +376,10 @@ function BlockStyleControls( { ); } -/** - * Override the default edit UI to include new inspector controls for - * all the custom styles configs. - * - * @param {Function} BlockEdit Original component. - * - * @return {Function} Wrapped component. - */ -export const withBlockStyleControls = createHigherOrderComponent( - ( BlockEdit ) => ( props ) => { - if ( ! hasStyleSupport( props.name ) ) { - return ; - } - - const shouldDisplayControls = useDisplayBlockControls(); - const blockEditingMode = useBlockEditingMode(); - - return ( - <> - { shouldDisplayControls && blockEditingMode === 'default' && ( - - ) } - - - ); - }, - 'withBlockStyleControls' -); +export const BlockEdit = BlockStyleControls; +export function hasSupport( name ) { + return hasStyleSupport( name ); +} // Defines which element types are supported, including their hover styles or // any other elements that have been included under a single element type @@ -542,12 +521,6 @@ addFilter( addEditProps ); -addFilter( - 'editor.BlockEdit', - 'core/style/with-block-controls', - withBlockStyleControls -); - addFilter( 'editor.BlockListBlock', 'core/editor/with-elements-styles', From 70d34c48bb5ce955a66ed7c3868d45285b2af1d0 Mon Sep 17 00:00:00 2001 From: Ella Date: Thu, 7 Dec 2023 14:53:30 +0200 Subject: [PATCH 4/8] Avoid dynamic import --- packages/block-editor/src/hooks/align.js | 21 +++----- packages/block-editor/src/hooks/anchor.js | 12 +++-- .../block-editor/src/hooks/block-hooks.js | 10 ++-- .../block-editor/src/hooks/block-renaming.js | 12 +++-- .../src/hooks/custom-class-name.js | 12 +++-- .../block-editor/src/hooks/custom-fields.js | 22 +++++---- packages/block-editor/src/hooks/duotone.js | 17 +++---- packages/block-editor/src/hooks/index.js | 49 ++++++++++--------- packages/block-editor/src/hooks/layout.js | 17 +++---- packages/block-editor/src/hooks/position.js | 22 +++++---- packages/block-editor/src/hooks/style.js | 8 +-- 11 files changed, 102 insertions(+), 100 deletions(-) diff --git a/packages/block-editor/src/hooks/align.js b/packages/block-editor/src/hooks/align.js index 5cd4e6ca940a2e..e4046d4acb19cf 100644 --- a/packages/block-editor/src/hooks/align.js +++ b/packages/block-editor/src/hooks/align.js @@ -6,7 +6,7 @@ import classnames from 'classnames'; /** * WordPress dependencies */ -import { createHigherOrderComponent, pure } from '@wordpress/compose'; +import { createHigherOrderComponent } from '@wordpress/compose'; import { addFilter } from '@wordpress/hooks'; import { getBlockSupport, @@ -152,18 +152,13 @@ function BlockEditAlignmentToolbarControlsPure( { ); } -// We don't want block controls to re-render when typing inside a block. `pure` -// will prevent re-renders unless props change, so only pass the needed props -// and not the whole attributes object. -const BlockEditAlignmentToolbarControls = pure( - BlockEditAlignmentToolbarControlsPure -); - -export const BlockEdit = BlockEditAlignmentToolbarControls; -export const attributeKeys = [ 'align' ]; -export function hasSupport( blockName ) { - return hasBlockSupport( blockName, 'align', false ); -} +export default { + edit: BlockEditAlignmentToolbarControlsPure, + attributeKeys: [ 'align' ], + hasSupport( name ) { + return hasBlockSupport( name, 'align', false ); + }, +}; function BlockListBlockWithDataAlign( { block: BlockListBlock, props } ) { const { name, attributes } = props; diff --git a/packages/block-editor/src/hooks/anchor.js b/packages/block-editor/src/hooks/anchor.js index dce1aff46b1506..8bfd28e68765d2 100644 --- a/packages/block-editor/src/hooks/anchor.js +++ b/packages/block-editor/src/hooks/anchor.js @@ -115,11 +115,13 @@ function BlockEditAnchorControlPure( { blockName, anchor, setAttributes } ) { ); } -export const BlockEdit = BlockEditAnchorControlPure; -export const attributeKeys = [ 'anchor' ]; -export function hasSupport( name ) { - return hasBlockSupport( name, 'anchor' ); -} +export default { + edit: BlockEditAnchorControlPure, + attributeKeys: [ 'anchor' ], + hasSupport( name ) { + return hasBlockSupport( name, 'anchor' ); + }, +}; /** * Override props assigned to save component to inject anchor ID, if block diff --git a/packages/block-editor/src/hooks/block-hooks.js b/packages/block-editor/src/hooks/block-hooks.js index 4647f0f74e8a45..ec62c930719797 100644 --- a/packages/block-editor/src/hooks/block-hooks.js +++ b/packages/block-editor/src/hooks/block-hooks.js @@ -233,7 +233,9 @@ function BlockHooksControlPure( props ) { ); } -export const BlockEdit = BlockHooksControlPure; -export function hasSupport() { - return true; -} +export default { + edit: BlockHooksControlPure, + hasSupport() { + return true; + }, +}; diff --git a/packages/block-editor/src/hooks/block-renaming.js b/packages/block-editor/src/hooks/block-renaming.js index c48e835a0cb937..26ada6ba732819 100644 --- a/packages/block-editor/src/hooks/block-renaming.js +++ b/packages/block-editor/src/hooks/block-renaming.js @@ -62,11 +62,13 @@ function BlockRenameControlPure( { metadata, setAttributes } ) { ); } -export const BlockEdit = BlockRenameControlPure; -export const attributeKeys = [ 'metadata' ]; -export function hasSupport( name ) { - return hasBlockSupport( name, 'renaming', true ); -} +export default { + edit: BlockRenameControlPure, + attributeKeys: [ 'metadata' ], + hasSupport( name ) { + return hasBlockSupport( name, 'renaming', true ); + }, +}; addFilter( 'blocks.registerBlockType', diff --git a/packages/block-editor/src/hooks/custom-class-name.js b/packages/block-editor/src/hooks/custom-class-name.js index 070b75da84277d..331edd9ef214a2 100644 --- a/packages/block-editor/src/hooks/custom-class-name.js +++ b/packages/block-editor/src/hooks/custom-class-name.js @@ -63,11 +63,13 @@ function CustomClassNameControlsPure( { className, setAttributes } ) { ); } -export const BlockEdit = CustomClassNameControlsPure; -export const attributeKeys = [ 'className' ]; -export function hasSupport( name ) { - return hasBlockSupport( name, 'customClassName', true ); -} +export default { + edit: CustomClassNameControlsPure, + attributeKeys: [ 'className' ], + hasSupport( name ) { + return hasBlockSupport( name, 'customClassName', true ); + }, +}; /** * Override props assigned to save component to inject the className, if block diff --git a/packages/block-editor/src/hooks/custom-fields.js b/packages/block-editor/src/hooks/custom-fields.js index c7e68507ffffc2..9b677933adc138 100644 --- a/packages/block-editor/src/hooks/custom-fields.js +++ b/packages/block-editor/src/hooks/custom-fields.js @@ -90,16 +90,18 @@ function CustomFieldsControlPure( { name, connections, setAttributes } ) { ); } -export const BlockEdit = CustomFieldsControlPure; -export const attributeKeys = [ 'connections' ]; -export function hasSupport( name ) { - return ( - hasBlockSupport( name, '__experimentalConnections', false ) && - // Check if the current block is a paragraph or image block. - // Currently, only these two blocks are supported. - [ 'core/paragraph', 'core/image' ].includes( name ) - ); -} +export default { + edit: CustomFieldsControlPure, + attributeKeys: [ 'connections' ], + hasSupport( name ) { + return ( + hasBlockSupport( name, '__experimentalConnections', false ) && + // Check if the current block is a paragraph or image block. + // Currently, only these two blocks are supported. + [ 'core/paragraph', 'core/image' ].includes( name ) + ); + }, +}; if ( window.__experimentalConnections || diff --git a/packages/block-editor/src/hooks/duotone.js b/packages/block-editor/src/hooks/duotone.js index c5efb0466b09c6..a574f7d6e9ef72 100644 --- a/packages/block-editor/src/hooks/duotone.js +++ b/packages/block-editor/src/hooks/duotone.js @@ -175,16 +175,13 @@ function DuotonePanelPure( { style, setAttributes, name } ) { ); } -export const attributeKeys = [ 'style' ]; - -export function hasSupport( name ) { - return hasBlockSupport( name, 'filter.duotone' ); -} - -// We don't want block controls to re-render when typing inside a block. `pure` -// will prevent re-renders unless props change, so only pass the needed props -// and not the whole attributes object. -export const BlockEdit = DuotonePanelPure; +export default { + edit: DuotonePanelPure, + attributeKeys: [ 'style' ], + hasSupport( name ) { + return hasBlockSupport( name, 'filter.duotone' ); + }, +}; /** * Filters registered block settings, extending attributes to include diff --git a/packages/block-editor/src/hooks/index.js b/packages/block-editor/src/hooks/index.js index a7dc653174f22d..84aece37b04833 100644 --- a/packages/block-editor/src/hooks/index.js +++ b/packages/block-editor/src/hooks/index.js @@ -9,43 +9,44 @@ import { addFilter } from '@wordpress/hooks'; */ import useDisplayBlockControls from '../components/use-display-block-controls'; import './compat'; +import align from './align'; import './lock'; -import './anchor'; +import anchor from './anchor'; import './aria-label'; -import './custom-class-name'; +import customClassName from './custom-class-name'; import './generated-class-name'; -import './style'; +import style from './style'; import './settings'; import './color'; +import duotone from './duotone'; import './font-family'; import './font-size'; import './border'; -import './position'; +import position from './position'; +import layout from './layout'; import './content-lock-ui'; import './metadata'; -import './custom-fields'; -import './block-hooks'; -import './block-renaming'; +import customFields from './custom-fields'; +import blockHooks from './block-hooks'; +import blockRenaming from './block-renaming'; const features = [ - 'layout', - 'duotone', - 'align', - 'anchor', - 'block-hooks', - 'block-renaming', - 'custom-class-name', - window.__experimentalConnections ? 'custom-fields' : null, - 'position', - 'style', + align, + anchor, + customClassName, + style, + duotone, + position, + layout, + window.__experimentalConnections ? customFields : null, + blockHooks, + blockRenaming, ] .filter( Boolean ) - .map( ( feature ) => { - const settings = require( `./${ feature }` ); + .map( ( settings ) => { return { ...settings, - name: feature, - BlockEdit: pure( settings.BlockEdit ), + Edit: pure( settings.edit ), }; } ); @@ -63,7 +64,7 @@ export const withBlockEditHooks = createHigherOrderComponent( const shouldDisplayControls = useDisplayBlockControls(); return [ ...features.map( - ( { name, BlockEdit, hasSupport, attributeKeys = [] } ) => { + ( { Edit, hasSupport, attributeKeys = [] }, i ) => { if ( ! shouldDisplayControls || ! hasSupport( props.name ) @@ -77,8 +78,8 @@ export const withBlockEditHooks = createHigherOrderComponent( } } return ( - { - const isPositionDisabled = useIsPositionDisabled( props ); - if ( isPositionDisabled ) { - return; - } - return ; +export default { + edit: function Edit( props ) { + const isPositionDisabled = useIsPositionDisabled( props ); + if ( isPositionDisabled ) { + return; + } + return ; + }, + attributeKeys: [ 'style' ], + hasSupport( name ) { + return hasBlockSupport( name, POSITION_SUPPORT_KEY ); + }, }; -export const attributeKeys = [ 'style' ]; -export function hasSupport( name ) { - return hasBlockSupport( name, POSITION_SUPPORT_KEY ); -} // We don't want block controls to re-render when typing inside a block. `pure` // will prevent re-renders unless props change, so only pass the needed props diff --git a/packages/block-editor/src/hooks/style.js b/packages/block-editor/src/hooks/style.js index d9ad95b3e15684..40363423168874 100644 --- a/packages/block-editor/src/hooks/style.js +++ b/packages/block-editor/src/hooks/style.js @@ -376,10 +376,10 @@ function BlockStyleControls( { ); } -export const BlockEdit = BlockStyleControls; -export function hasSupport( name ) { - return hasStyleSupport( name ); -} +export default { + edit: BlockStyleControls, + hasSupport: hasStyleSupport, +}; // Defines which element types are supported, including their hover styles or // any other elements that have been included under a single element type From 4ab0f932e9295fac454ebce0ba5369cf0da14a2e Mon Sep 17 00:00:00 2001 From: Ella Date: Thu, 7 Dec 2023 14:56:01 +0200 Subject: [PATCH 5/8] null return --- packages/block-editor/src/hooks/position.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-editor/src/hooks/position.js b/packages/block-editor/src/hooks/position.js index 45fbadf8fe13e3..8a0c4246e9efe3 100644 --- a/packages/block-editor/src/hooks/position.js +++ b/packages/block-editor/src/hooks/position.js @@ -322,7 +322,7 @@ export default { edit: function Edit( props ) { const isPositionDisabled = useIsPositionDisabled( props ); if ( isPositionDisabled ) { - return; + return null; } return ; }, From e6296591ea25bc1b53e5e5873c68efbfb6ff7a24 Mon Sep 17 00:00:00 2001 From: Ella Date: Thu, 7 Dec 2023 15:58:41 +0200 Subject: [PATCH 6/8] Fix prop rename issue --- packages/block-editor/src/hooks/align.js | 2 +- packages/block-editor/src/hooks/anchor.js | 6 +- .../block-editor/src/hooks/block-hooks.js | 43 +++++------ packages/block-editor/src/hooks/position.js | 13 +--- packages/block-editor/src/hooks/test/align.js | 72 +------------------ 5 files changed, 26 insertions(+), 110 deletions(-) diff --git a/packages/block-editor/src/hooks/align.js b/packages/block-editor/src/hooks/align.js index e4046d4acb19cf..1358473c1b1b83 100644 --- a/packages/block-editor/src/hooks/align.js +++ b/packages/block-editor/src/hooks/align.js @@ -109,7 +109,7 @@ export function addAttribute( settings ) { } function BlockEditAlignmentToolbarControlsPure( { - blockName, + name: blockName, align, setAttributes, } ) { diff --git a/packages/block-editor/src/hooks/anchor.js b/packages/block-editor/src/hooks/anchor.js index 8bfd28e68765d2..882820678aa870 100644 --- a/packages/block-editor/src/hooks/anchor.js +++ b/packages/block-editor/src/hooks/anchor.js @@ -51,7 +51,11 @@ export function addAttribute( settings ) { return settings; } -function BlockEditAnchorControlPure( { blockName, anchor, setAttributes } ) { +function BlockEditAnchorControlPure( { + name: blockName, + anchor, + setAttributes, +} ) { const blockEditingMode = useBlockEditingMode(); const isWeb = Platform.OS === 'web'; diff --git a/packages/block-editor/src/hooks/block-hooks.js b/packages/block-editor/src/hooks/block-hooks.js index ec62c930719797..a1640c72f4b2b4 100644 --- a/packages/block-editor/src/hooks/block-hooks.js +++ b/packages/block-editor/src/hooks/block-hooks.js @@ -19,7 +19,7 @@ import { store as blockEditorStore } from '../store'; const EMPTY_OBJECT = {}; -function BlockHooksControlPure( props ) { +function BlockHooksControlPure( { name, clientId } ) { const blockTypes = useSelect( ( select ) => select( blocksStore ).getBlockTypes(), [] @@ -28,10 +28,9 @@ function BlockHooksControlPure( props ) { const hookedBlocksForCurrentBlock = useMemo( () => blockTypes?.filter( - ( { blockHooks } ) => - blockHooks && props.blockName in blockHooks + ( { blockHooks } ) => blockHooks && name in blockHooks ), - [ blockTypes, props.blockName ] + [ blockTypes, name ] ); const { blockIndex, rootClientId, innerBlocksLength } = useSelect( @@ -40,13 +39,12 @@ function BlockHooksControlPure( props ) { select( blockEditorStore ); return { - blockIndex: getBlockIndex( props.clientId ), - innerBlocksLength: getBlock( props.clientId )?.innerBlocks - ?.length, - rootClientId: getBlockRootClientId( props.clientId ), + blockIndex: getBlockIndex( clientId ), + innerBlocksLength: getBlock( clientId )?.innerBlocks?.length, + rootClientId: getBlockRootClientId( clientId ), }; }, - [ props.clientId ] + [ clientId ] ); const hookedBlockClientIds = useSelect( @@ -63,8 +61,7 @@ function BlockHooksControlPure( props ) { return clientIds; } - const relativePosition = - block?.blockHooks?.[ props.blockName ]; + const relativePosition = block?.blockHooks?.[ name ]; let candidates; switch ( relativePosition ) { @@ -81,12 +78,12 @@ function BlockHooksControlPure( props ) { // Any of the current block's child blocks (with the right block type) qualifies // as a hooked first or last child block, as the block might've been automatically // inserted and then moved around a bit by the user. - candidates = getBlock( props.clientId ).innerBlocks; + candidates = getBlock( clientId ).innerBlocks; break; } const hookedBlock = candidates?.find( - ( { name } ) => name === block.name + ( candidate ) => name === candidate.name ); // If the block exists in the designated location, we consider it hooked @@ -116,12 +113,7 @@ function BlockHooksControlPure( props ) { return EMPTY_OBJECT; }, - [ - hookedBlocksForCurrentBlock, - props.blockName, - props.clientId, - rootClientId, - ] + [ hookedBlocksForCurrentBlock, name, clientId, rootClientId ] ); const { insertBlock, removeBlock } = useDispatch( blockEditorStore ); @@ -167,7 +159,7 @@ function BlockHooksControlPure( props ) { block, // TODO: It'd be great if insertBlock() would accept negative indices for insertion. relativePosition === 'first_child' ? 0 : innerBlocksLength, - props.clientId, // Insert as a child of the current block. + clientId, // Insert as a child of the current block. false ); break; @@ -205,9 +197,7 @@ function BlockHooksControlPure( props ) { if ( ! checked ) { // Create and insert block. const relativePosition = - block.blockHooks[ - props.blockName - ]; + block.blockHooks[ name ]; insertBlockIntoDesignatedLocation( createBlock( block.name ), relativePosition @@ -216,11 +206,12 @@ function BlockHooksControlPure( props ) { } // Remove block. - const clientId = + removeBlock( hookedBlockClientIds[ block.name - ]; - removeBlock( clientId, false ); + ], + false + ); } } /> ); diff --git a/packages/block-editor/src/hooks/position.js b/packages/block-editor/src/hooks/position.js index 8a0c4246e9efe3..cdeb90822f0ac4 100644 --- a/packages/block-editor/src/hooks/position.js +++ b/packages/block-editor/src/hooks/position.js @@ -12,11 +12,7 @@ import { BaseControl, privateApis as componentsPrivateApis, } from '@wordpress/components'; -import { - createHigherOrderComponent, - pure, - useInstanceId, -} from '@wordpress/compose'; +import { createHigherOrderComponent, useInstanceId } from '@wordpress/compose'; import { useSelect } from '@wordpress/data'; import { useMemo, Platform } from '@wordpress/element'; import { addFilter } from '@wordpress/hooks'; @@ -324,7 +320,7 @@ export default { if ( isPositionDisabled ) { return null; } - return ; + return ; }, attributeKeys: [ 'style' ], hasSupport( name ) { @@ -332,11 +328,6 @@ export default { }, }; -// We don't want block controls to re-render when typing inside a block. `pure` -// will prevent re-renders unless props change, so only pass the needed props -// and not the whole attributes object. -const PositionPanel = pure( PositionPanelPure ); - /** * Override the default block element to add the position styles. * diff --git a/packages/block-editor/src/hooks/test/align.js b/packages/block-editor/src/hooks/test/align.js index b928e6eafc8b2d..c695399e993b0e 100644 --- a/packages/block-editor/src/hooks/test/align.js +++ b/packages/block-editor/src/hooks/test/align.js @@ -12,20 +12,12 @@ import { registerBlockType, unregisterBlockType, } from '@wordpress/blocks'; -import { SlotFillProvider } from '@wordpress/components'; /** * Internal dependencies */ -import BlockControls from '../../components/block-controls'; -import BlockEdit from '../../components/block-edit'; import BlockEditorProvider from '../../components/provider'; -import { - getValidAlignments, - withAlignmentControls, - withDataAlign, - addAssignedAlign, -} from '../align'; +import { getValidAlignments, withDataAlign, addAssignedAlign } from '../align'; const noop = () => {}; @@ -157,68 +149,6 @@ describe( 'align', () => { } ); } ); - describe( 'withAlignControls', () => { - const componentProps = { - name: 'core/foo', - attributes: {}, - isSelected: true, - }; - - it( 'should do nothing if no valid alignments', () => { - registerBlockType( 'core/foo', blockSettings ); - - const EnhancedComponent = withAlignmentControls( - ( { wrapperProps } ) =>
- ); - - render( - - - - - - - ); - - expect( - screen.queryByRole( 'button', { - name: 'Align', - expanded: false, - } ) - ).not.toBeInTheDocument(); - } ); - - it( 'should render toolbar controls if valid alignments', () => { - registerBlockType( 'core/foo', { - ...blockSettings, - supports: { - align: true, - alignWide: false, - }, - } ); - - const EnhancedComponent = withAlignmentControls( - ( { wrapperProps } ) =>
- ); - - render( - - - - - - - ); - - expect( - screen.getAllByRole( 'button', { - name: 'Align', - expanded: false, - } ) - ).toHaveLength( 2 ); - } ); - } ); - describe( 'withDataAlign', () => { it( 'should render with wrapper props', () => { registerBlockType( 'core/foo', { From 738c07aff0c3d63a0c4da68c974740757bde3ce3 Mon Sep 17 00:00:00 2001 From: Ella Date: Thu, 7 Dec 2023 18:07:16 +0200 Subject: [PATCH 7/8] Fix mobile --- .../block-editor/src/hooks/align.native.js | 1 + packages/block-editor/src/hooks/index.js | 81 ++++--------------- .../block-editor/src/hooks/index.native.js | 9 ++- packages/block-editor/src/hooks/utils.js | 59 ++++++++++++++ 4 files changed, 81 insertions(+), 69 deletions(-) diff --git a/packages/block-editor/src/hooks/align.native.js b/packages/block-editor/src/hooks/align.native.js index 1bf375b654ad40..7a229e79870a80 100644 --- a/packages/block-editor/src/hooks/align.native.js +++ b/packages/block-editor/src/hooks/align.native.js @@ -8,6 +8,7 @@ import { WIDE_ALIGNMENTS } from '@wordpress/components'; const ALIGNMENTS = [ 'left', 'center', 'right' ]; export * from './align.js'; +export { default } from './align.js'; // Used to filter out blocks that don't support wide/full alignment on mobile addFilter( diff --git a/packages/block-editor/src/hooks/index.js b/packages/block-editor/src/hooks/index.js index 84aece37b04833..6ae589dd672bf1 100644 --- a/packages/block-editor/src/hooks/index.js +++ b/packages/block-editor/src/hooks/index.js @@ -1,13 +1,7 @@ -/** - * WordPress dependencies - */ -import { createHigherOrderComponent, pure } from '@wordpress/compose'; -import { addFilter } from '@wordpress/hooks'; - /** * Internal dependencies */ -import useDisplayBlockControls from '../components/use-display-block-controls'; +import { createBlockEditFilter } from './utils'; import './compat'; import align from './align'; import './lock'; @@ -30,25 +24,20 @@ import customFields from './custom-fields'; import blockHooks from './block-hooks'; import blockRenaming from './block-renaming'; -const features = [ - align, - anchor, - customClassName, - style, - duotone, - position, - layout, - window.__experimentalConnections ? customFields : null, - blockHooks, - blockRenaming, -] - .filter( Boolean ) - .map( ( settings ) => { - return { - ...settings, - Edit: pure( settings.edit ), - }; - } ); +createBlockEditFilter( + [ + align, + anchor, + customClassName, + style, + duotone, + position, + layout, + window.__experimentalConnections ? customFields : null, + blockHooks, + blockRenaming, + ].filter( Boolean ) +); export { useCustomSides } from './dimensions'; export { useLayoutClasses, useLayoutStyles } from './layout'; @@ -58,43 +47,3 @@ export { getSpacingClassesAndStyles } from './use-spacing-props'; export { getTypographyClassesAndStyles } from './use-typography-props'; export { getGapCSSValue } from './gap'; export { useCachedTruthy } from './use-cached-truthy'; - -export const withBlockEditHooks = createHigherOrderComponent( - ( OriginalBlockEdit ) => ( props ) => { - const shouldDisplayControls = useDisplayBlockControls(); - return [ - ...features.map( - ( { Edit, hasSupport, attributeKeys = [] }, i ) => { - if ( - ! shouldDisplayControls || - ! hasSupport( props.name ) - ) { - return null; - } - const neededProps = {}; - for ( const key of attributeKeys ) { - if ( props.attributes[ key ] ) { - neededProps[ key ] = props.attributes[ key ]; - } - } - return ( - - ); - } - ), - , - ]; - }, - 'withBlockEditHooks' -); - -addFilter( 'editor.BlockEdit', 'core/editor/hooks', withBlockEditHooks ); diff --git a/packages/block-editor/src/hooks/index.native.js b/packages/block-editor/src/hooks/index.native.js index 42bda25bfe1ccf..3f1a4473c13891 100644 --- a/packages/block-editor/src/hooks/index.native.js +++ b/packages/block-editor/src/hooks/index.native.js @@ -1,16 +1,19 @@ /** * Internal dependencies */ +import { createBlockEditFilter } from './utils'; import './compat'; -import './align'; -import './anchor'; +import align from './align'; +import anchor from './anchor'; import './custom-class-name'; import './generated-class-name'; -import './style'; +import style from './style'; import './color'; import './font-size'; import './layout'; +createBlockEditFilter( [ align, anchor, style ] ); + export { getBorderClassesAndStyles, useBorderProps } from './use-border-props'; export { getColorClassesAndStyles, useColorProps } from './use-color-props'; export { getSpacingClassesAndStyles } from './use-spacing-props'; diff --git a/packages/block-editor/src/hooks/utils.js b/packages/block-editor/src/hooks/utils.js index 5985b821d00a86..a7cfd764f96b2f 100644 --- a/packages/block-editor/src/hooks/utils.js +++ b/packages/block-editor/src/hooks/utils.js @@ -4,10 +4,13 @@ import { getBlockSupport } from '@wordpress/blocks'; import { useMemo, useEffect, useId } from '@wordpress/element'; import { useDispatch } from '@wordpress/data'; +import { createHigherOrderComponent, pure } from '@wordpress/compose'; +import { addFilter } from '@wordpress/hooks'; /** * Internal dependencies */ +import useDisplayBlockControls from '../components/use-display-block-controls'; import { useSettings } from '../components'; import { useSettingsForBlockElement } from '../components/global-styles/hooks'; import { getValueFromObjectPath, setImmutably } from '../utils/object'; @@ -362,3 +365,59 @@ export function useBlockSettings( name, parentLayout ) { return useSettingsForBlockElement( rawSettings, name ); } + +export function createBlockEditFilter( features ) { + // We don't want block controls to re-render when typing inside a block. + // `pure` will prevent re-renders unless props change, so only pass the + // needed props and not the whole attributes object. + features = features.map( ( settings ) => { + return { ...settings, Edit: pure( settings.edit ) }; + } ); + const withBlockEditHooks = createHigherOrderComponent( + ( OriginalBlockEdit ) => ( props ) => { + const shouldDisplayControls = useDisplayBlockControls(); + // CAUTION: code added before this line will be executed for all + // blocks, not just those that support the feature! Code added + // above this line should be carefully evaluated for its impact on + // performance. + return [ + ...features.map( + ( { Edit, hasSupport, attributeKeys = [] }, i ) => { + if ( + ! shouldDisplayControls || + ! hasSupport( props.name ) + ) { + return null; + } + + const neededProps = {}; + for ( const key of attributeKeys ) { + if ( props.attributes[ key ] ) { + neededProps[ key ] = props.attributes[ key ]; + } + } + return ( + + ); + } + ), + , + ]; + }, + 'withBlockEditHooks' + ); + addFilter( 'editor.BlockEdit', 'core/editor/hooks', withBlockEditHooks ); +} From aa5e8f550e0a9d6e306f7b3c82600796137b78dd Mon Sep 17 00:00:00 2001 From: Ella Date: Thu, 7 Dec 2023 20:14:17 +0200 Subject: [PATCH 8/8] Fix share controls with children --- .../src/components/block-controls/hook.js | 30 +------- .../components/block-info-slot-fill/index.js | 2 +- .../src/components/inspector-controls/fill.js | 2 +- .../inspector-controls/fill.native.js | 2 +- .../use-display-block-controls/index.js | 28 +++++--- .../index.native.js | 6 +- packages/block-editor/src/hooks/align.js | 1 + packages/block-editor/src/hooks/duotone.js | 1 + packages/block-editor/src/hooks/layout.js | 1 + packages/block-editor/src/hooks/utils.js | 71 +++++++++++-------- 10 files changed, 66 insertions(+), 78 deletions(-) diff --git a/packages/block-editor/src/components/block-controls/hook.js b/packages/block-editor/src/components/block-controls/hook.js index 18a38e245e58ab..e3f69c8bec3b25 100644 --- a/packages/block-editor/src/components/block-controls/hook.js +++ b/packages/block-editor/src/components/block-controls/hook.js @@ -1,45 +1,19 @@ /** * WordPress dependencies */ -import { store as blocksStore } from '@wordpress/blocks'; -import { useSelect } from '@wordpress/data'; /** * Internal dependencies */ import groups from './groups'; -import { store as blockEditorStore } from '../../store'; -import { useBlockEditContext } from '../block-edit/context'; import useDisplayBlockControls from '../use-display-block-controls'; export default function useBlockControlsFill( group, shareWithChildBlocks ) { - const isDisplayed = useDisplayBlockControls(); - const { clientId } = useBlockEditContext(); - const isParentDisplayed = useSelect( - ( select ) => { - if ( ! shareWithChildBlocks ) { - return false; - } - - const { getBlockName, hasSelectedInnerBlock } = - select( blockEditorStore ); - const { hasBlockSupport } = select( blocksStore ); - - return ( - hasBlockSupport( - getBlockName( clientId ), - '__experimentalExposeControlsToChildren', - false - ) && hasSelectedInnerBlock( clientId ) - ); - }, - [ shareWithChildBlocks, clientId ] - ); - + const { isDisplayed, isParentDisplayed } = useDisplayBlockControls(); if ( isDisplayed ) { return groups[ group ]?.Fill; } - if ( isParentDisplayed ) { + if ( isParentDisplayed && shareWithChildBlocks ) { return groups.parent.Fill; } return null; diff --git a/packages/block-editor/src/components/block-info-slot-fill/index.js b/packages/block-editor/src/components/block-info-slot-fill/index.js index db7919b6ef5eab..8e16757f3ebbc1 100644 --- a/packages/block-editor/src/components/block-info-slot-fill/index.js +++ b/packages/block-editor/src/components/block-info-slot-fill/index.js @@ -13,7 +13,7 @@ const { createPrivateSlotFill } = unlock( componentsPrivateApis ); const { Fill, Slot } = createPrivateSlotFill( 'BlockInformation' ); const BlockInfo = ( props ) => { - const isDisplayed = useDisplayBlockControls(); + const { isDisplayed } = useDisplayBlockControls(); if ( ! isDisplayed ) { return null; } diff --git a/packages/block-editor/src/components/inspector-controls/fill.js b/packages/block-editor/src/components/inspector-controls/fill.js index f0640a9d31ddc5..fdb0d44f0eccb6 100644 --- a/packages/block-editor/src/components/inspector-controls/fill.js +++ b/packages/block-editor/src/components/inspector-controls/fill.js @@ -33,7 +33,7 @@ export default function InspectorControlsFill( { group = __experimentalGroup; } - const isDisplayed = useDisplayBlockControls(); + const { isDisplayed } = useDisplayBlockControls(); const Fill = groups[ group ]?.Fill; if ( ! Fill ) { warning( `Unknown InspectorControls group "${ group }" provided.` ); diff --git a/packages/block-editor/src/components/inspector-controls/fill.native.js b/packages/block-editor/src/components/inspector-controls/fill.native.js index d38d865cd15cc0..f1ee5a14cd18e1 100644 --- a/packages/block-editor/src/components/inspector-controls/fill.native.js +++ b/packages/block-editor/src/components/inspector-controls/fill.native.js @@ -35,7 +35,7 @@ export default function InspectorControlsFill( { ); group = __experimentalGroup; } - const isDisplayed = useDisplayBlockControls(); + const { isDisplayed } = useDisplayBlockControls(); const Fill = groups[ group ]?.Fill; if ( ! Fill ) { diff --git a/packages/block-editor/src/components/use-display-block-controls/index.js b/packages/block-editor/src/components/use-display-block-controls/index.js index 605556f295b968..ef27479593a736 100644 --- a/packages/block-editor/src/components/use-display-block-controls/index.js +++ b/packages/block-editor/src/components/use-display-block-controls/index.js @@ -2,6 +2,7 @@ * WordPress dependencies */ import { useSelect } from '@wordpress/data'; +import { store as blocksStore } from '@wordpress/blocks'; /** * Internal dependencies @@ -13,23 +14,28 @@ export default function useDisplayBlockControls() { const { isSelected, clientId, name } = useBlockEditContext(); return useSelect( ( select ) => { - if ( isSelected ) { - return true; - } - const { getBlockName, isFirstMultiSelectedBlock, getMultiSelectedBlockClientIds, + hasSelectedInnerBlock, } = select( blockEditorStore ); + const { hasBlockSupport } = select( blocksStore ); - if ( isFirstMultiSelectedBlock( clientId ) ) { - return getMultiSelectedBlockClientIds().every( - ( id ) => getBlockName( id ) === name - ); - } - - return false; + return { + isDisplayed: + isSelected || + ( isFirstMultiSelectedBlock( clientId ) && + getMultiSelectedBlockClientIds().every( + ( id ) => getBlockName( id ) === name + ) ), + isParentDisplayed: + hasBlockSupport( + getBlockName( clientId ), + '__experimentalExposeControlsToChildren', + false + ) && hasSelectedInnerBlock( clientId ), + }; }, [ clientId, isSelected, name ] ); diff --git a/packages/block-editor/src/components/use-display-block-controls/index.native.js b/packages/block-editor/src/components/use-display-block-controls/index.native.js index e8a198e1592e82..d865ed6d9d7b26 100644 --- a/packages/block-editor/src/components/use-display-block-controls/index.native.js +++ b/packages/block-editor/src/components/use-display-block-controls/index.native.js @@ -26,11 +26,7 @@ export default function useDisplayBlockControls() { false ); - if ( ! hideControls && isSelected ) { - return true; - } - - return false; + return { isDisplayed: ! hideControls && isSelected }; }, [ clientId, isSelected, name ] ); diff --git a/packages/block-editor/src/hooks/align.js b/packages/block-editor/src/hooks/align.js index 1358473c1b1b83..2019228cf2d3e8 100644 --- a/packages/block-editor/src/hooks/align.js +++ b/packages/block-editor/src/hooks/align.js @@ -153,6 +153,7 @@ function BlockEditAlignmentToolbarControlsPure( { } export default { + shareWithChildBlocks: true, edit: BlockEditAlignmentToolbarControlsPure, attributeKeys: [ 'align' ], hasSupport( name ) { diff --git a/packages/block-editor/src/hooks/duotone.js b/packages/block-editor/src/hooks/duotone.js index a574f7d6e9ef72..c0b76d12cb3707 100644 --- a/packages/block-editor/src/hooks/duotone.js +++ b/packages/block-editor/src/hooks/duotone.js @@ -176,6 +176,7 @@ function DuotonePanelPure( { style, setAttributes, name } ) { } export default { + shareWithChildBlocks: true, edit: DuotonePanelPure, attributeKeys: [ 'style' ], hasSupport( name ) { diff --git a/packages/block-editor/src/hooks/layout.js b/packages/block-editor/src/hooks/layout.js index f46d7683541f42..46239e1de07037 100644 --- a/packages/block-editor/src/hooks/layout.js +++ b/packages/block-editor/src/hooks/layout.js @@ -287,6 +287,7 @@ function LayoutPanelPure( { layout, setAttributes, name: blockName } ) { } export default { + shareWithChildBlocks: true, edit: LayoutPanelPure, attributeKeys: [ 'layout' ], hasSupport( name ) { diff --git a/packages/block-editor/src/hooks/utils.js b/packages/block-editor/src/hooks/utils.js index a7cfd764f96b2f..98638ae5dabf54 100644 --- a/packages/block-editor/src/hooks/utils.js +++ b/packages/block-editor/src/hooks/utils.js @@ -375,45 +375,54 @@ export function createBlockEditFilter( features ) { } ); const withBlockEditHooks = createHigherOrderComponent( ( OriginalBlockEdit ) => ( props ) => { - const shouldDisplayControls = useDisplayBlockControls(); + const { isDisplayed, isParentDisplayed } = + useDisplayBlockControls(); // CAUTION: code added before this line will be executed for all // blocks, not just those that support the feature! Code added // above this line should be carefully evaluated for its impact on // performance. return [ - ...features.map( - ( { Edit, hasSupport, attributeKeys = [] }, i ) => { - if ( - ! shouldDisplayControls || - ! hasSupport( props.name ) - ) { - return null; - } + ...features.map( ( feature, i ) => { + const { + Edit, + hasSupport, + attributeKeys = [], + shareWithChildBlocks, + } = feature; + const shouldDisplayControls = + isDisplayed || + ( isParentDisplayed && shareWithChildBlocks ); - const neededProps = {}; - for ( const key of attributeKeys ) { - if ( props.attributes[ key ] ) { - neededProps[ key ] = props.attributes[ key ]; - } + if ( + ! shouldDisplayControls || + ! hasSupport( props.name ) + ) { + return null; + } + + const neededProps = {}; + for ( const key of attributeKeys ) { + if ( props.attributes[ key ] ) { + neededProps[ key ] = props.attributes[ key ]; } - return ( - - ); } - ), + return ( + + ); + } ), , ]; },