From 37e12631e273ce01769f1859b7d047e690b975a0 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Wed, 1 Nov 2023 15:19:13 +0400 Subject: [PATCH 1/6] Block Editor: Optimize layout style renderer subscription --- packages/block-editor/src/hooks/layout.js | 221 ++++++++++++---------- 1 file changed, 120 insertions(+), 101 deletions(-) diff --git a/packages/block-editor/src/hooks/layout.js b/packages/block-editor/src/hooks/layout.js index c8e81d38283927..005f00037205d7 100644 --- a/packages/block-editor/src/hooks/layout.js +++ b/packages/block-editor/src/hooks/layout.js @@ -345,6 +345,70 @@ export const withLayoutControls = createHigherOrderComponent( 'withLayoutControls' ); +function BlockListWithLayoutStyles( { block: BlockListBlock, props } ) { + const { name, attributes } = props; + const disableLayoutStyles = useSelect( ( select ) => { + const { getSettings } = select( blockEditorStore ); + return !! getSettings().disableLayoutStyles; + } ); + + const id = useInstanceId( BlockListBlock ); + const { layout } = attributes; + const { default: defaultBlockLayout } = + getBlockSupport( name, layoutBlockSupportKey ) || {}; + const usedLayout = + layout?.inherit || layout?.contentSize || layout?.wideSize + ? { ...layout, type: 'constrained' } + : layout || defaultBlockLayout || {}; + const layoutClasses = useLayoutClasses( attributes, name ); + + // Higher specificity to override defaults from theme.json. + const selector = `.wp-container-${ id }.wp-container-${ id }`; + const [ blockGapSupport ] = useSettings( 'spacing.blockGap' ); + const hasBlockGapSupport = blockGapSupport !== null; + + // Get CSS string for the current layout type. + // The CSS and `style` element is only output if it is not empty. + let css; + if ( ! disableLayoutStyles ) { + const fullLayoutType = getLayoutType( usedLayout?.type || 'default' ); + css = fullLayoutType?.getLayoutStyle?.( { + blockName: name, + selector, + layout: usedLayout, + style: attributes?.style, + hasBlockGapSupport, + } ); + } + + // Attach a `wp-container-` id-based class name as well as a layout class name such as `is-layout-flex`. + const layoutClassNames = classnames( + { + [ `wp-container-${ id }` ]: ! disableLayoutStyles && !! css, // Only attach a container class if there is generated CSS to be attached. + }, + layoutClasses + ); + + const { setStyleOverride, deleteStyleOverride } = unlock( + useDispatch( blockEditorStore ) + ); + + useEffect( () => { + if ( ! css ) return; + setStyleOverride( selector, { css } ); + return () => { + deleteStyleOverride( selector ); + }; + }, [ selector, css, setStyleOverride, deleteStyleOverride ] ); + + return ( + + ); +} + /** * Override the default block element to add the layout styles. * @@ -354,76 +418,63 @@ export const withLayoutControls = createHigherOrderComponent( */ export const withLayoutStyles = createHigherOrderComponent( ( BlockListBlock ) => ( props ) => { - const { name, attributes } = props; - const blockSupportsLayout = hasLayoutBlockSupport( name ); - const disableLayoutStyles = useSelect( ( select ) => { - const { getSettings } = select( blockEditorStore ); - return !! getSettings().disableLayoutStyles; - } ); - const shouldRenderLayoutStyles = - blockSupportsLayout && ! disableLayoutStyles; - const id = useInstanceId( BlockListBlock ); - const { layout } = attributes; - const { default: defaultBlockLayout } = - getBlockSupport( name, layoutBlockSupportKey ) || {}; - const usedLayout = - layout?.inherit || layout?.contentSize || layout?.wideSize - ? { ...layout, type: 'constrained' } - : layout || defaultBlockLayout || {}; - const layoutClasses = blockSupportsLayout - ? useLayoutClasses( attributes, name ) - : null; - // Higher specificity to override defaults from theme.json. - const selector = `.wp-container-${ id }.wp-container-${ id }`; - const [ blockGapSupport ] = useSettings( 'spacing.blockGap' ); - const hasBlockGapSupport = blockGapSupport !== null; - - // Get CSS string for the current layout type. - // The CSS and `style` element is only output if it is not empty. - let css; - if ( shouldRenderLayoutStyles ) { - const fullLayoutType = getLayoutType( - usedLayout?.type || 'default' - ); - css = fullLayoutType?.getLayoutStyle?.( { - blockName: name, - selector, - layout: usedLayout, - style: attributes?.style, - hasBlockGapSupport, - } ); + if ( ! hasLayoutBlockSupport( props.name ) ) { + return ; } - // Attach a `wp-container-` id-based class name as well as a layout class name such as `is-layout-flex`. - const layoutClassNames = classnames( - { - [ `wp-container-${ id }` ]: shouldRenderLayoutStyles && !! css, // Only attach a container class if there is generated CSS to be attached. - }, - layoutClasses - ); - - const { setStyleOverride, deleteStyleOverride } = unlock( - useDispatch( blockEditorStore ) - ); - - useEffect( () => { - if ( ! css ) return; - setStyleOverride( selector, { css } ); - return () => { - deleteStyleOverride( selector ); - }; - }, [ selector, css, setStyleOverride, deleteStyleOverride ] ); - return ( - ); }, 'withLayoutStyles' ); +function BlockListWithChildLayoutStyles( { block: BlockListBlock, props } ) { + const { style: { layout = {} } = {} } = props.attributes; + const { selfStretch, flexSize } = layout; + const disableLayoutStyles = useSelect( ( select ) => { + const { getSettings } = select( blockEditorStore ); + return !! getSettings().disableLayoutStyles; + } ); + + const id = useInstanceId( BlockListBlock ); + const selector = `.wp-container-content-${ id }`; + + let css = ''; + if ( selfStretch === 'fixed' && flexSize ) { + css += `${ selector } { + flex-basis: ${ flexSize }; + box-sizing: border-box; + }`; + } else if ( selfStretch === 'fill' ) { + css += `${ selector } { + flex-grow: 1; + }`; + } + + // Attach a `wp-container-content` id-based classname. + const className = classnames( props?.className, { + [ `wp-container-content-${ id }` ]: ! disableLayoutStyles && !! css, // Only attach a container class if there is generated CSS to be attached. + } ); + + const { setStyleOverride, deleteStyleOverride } = unlock( + useDispatch( blockEditorStore ) + ); + + useEffect( () => { + if ( ! css ) return; + setStyleOverride( selector, { css } ); + return () => { + deleteStyleOverride( selector ); + }; + }, [ selector, css, setStyleOverride, deleteStyleOverride ] ); + + return ; +} + /** * Override the default block element to add the child layout styles. * @@ -433,52 +484,20 @@ export const withLayoutStyles = createHigherOrderComponent( */ export const withChildLayoutStyles = createHigherOrderComponent( ( BlockListBlock ) => ( props ) => { - const { attributes } = props; - const { style: { layout = {} } = {} } = attributes; + const { style: { layout = {} } = {} } = props.attributes; const { selfStretch, flexSize } = layout; const hasChildLayout = selfStretch || flexSize; - const disableLayoutStyles = useSelect( ( select ) => { - const { getSettings } = select( blockEditorStore ); - return !! getSettings().disableLayoutStyles; - } ); - const shouldRenderChildLayoutStyles = - hasChildLayout && ! disableLayoutStyles; - - const id = useInstanceId( BlockListBlock ); - const selector = `.wp-container-content-${ id }`; - let css = ''; - - if ( selfStretch === 'fixed' && flexSize ) { - css += `${ selector } { - flex-basis: ${ flexSize }; - box-sizing: border-box; - }`; - } else if ( selfStretch === 'fill' ) { - css += `${ selector } { - flex-grow: 1; - }`; + if ( ! hasChildLayout ) { + return ; } - // Attach a `wp-container-content` id-based classname. - const className = classnames( props?.className, { - [ `wp-container-content-${ id }` ]: - shouldRenderChildLayoutStyles && !! css, // Only attach a container class if there is generated CSS to be attached. - } ); - - const { setStyleOverride, deleteStyleOverride } = unlock( - useDispatch( blockEditorStore ) + return ( + ); - - useEffect( () => { - if ( ! css ) return; - setStyleOverride( selector, { css } ); - return () => { - deleteStyleOverride( selector ); - }; - }, [ selector, css, setStyleOverride, deleteStyleOverride ] ); - - return ; }, 'withChildLayoutStyles' ); From 3d688d013fb28597f3a2bb6f12ef1b414b352993 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Thu, 2 Nov 2023 11:54:31 +0400 Subject: [PATCH 2/6] Rename components --- packages/block-editor/src/hooks/layout.js | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/packages/block-editor/src/hooks/layout.js b/packages/block-editor/src/hooks/layout.js index 005f00037205d7..9ad753efca27c8 100644 --- a/packages/block-editor/src/hooks/layout.js +++ b/packages/block-editor/src/hooks/layout.js @@ -345,7 +345,7 @@ export const withLayoutControls = createHigherOrderComponent( 'withLayoutControls' ); -function BlockListWithLayoutStyles( { block: BlockListBlock, props } ) { +function BlockWithLayoutStyles( { block: BlockListBlock, props } ) { const { name, attributes } = props; const disableLayoutStyles = useSelect( ( select ) => { const { getSettings } = select( blockEditorStore ); @@ -423,16 +423,13 @@ export const withLayoutStyles = createHigherOrderComponent( } return ( - + ); }, 'withLayoutStyles' ); -function BlockListWithChildLayoutStyles( { block: BlockListBlock, props } ) { +function BlockWithChildLayoutStyles( { block: BlockListBlock, props } ) { const { style: { layout = {} } = {} } = props.attributes; const { selfStretch, flexSize } = layout; const disableLayoutStyles = useSelect( ( select ) => { @@ -493,7 +490,7 @@ export const withChildLayoutStyles = createHigherOrderComponent( } return ( - From 8104743f4ececd1a38296761a8dccc524c1b8e51 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Thu, 2 Nov 2023 11:56:05 +0400 Subject: [PATCH 3/6] Swap destructuring with optional chaining --- packages/block-editor/src/hooks/layout.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-editor/src/hooks/layout.js b/packages/block-editor/src/hooks/layout.js index 9ad753efca27c8..6199d9f15bf8c7 100644 --- a/packages/block-editor/src/hooks/layout.js +++ b/packages/block-editor/src/hooks/layout.js @@ -481,7 +481,7 @@ function BlockWithChildLayoutStyles( { block: BlockListBlock, props } ) { */ export const withChildLayoutStyles = createHigherOrderComponent( ( BlockListBlock ) => ( props ) => { - const { style: { layout = {} } = {} } = props.attributes; + const layout = props.attributes.style?.layout ?? {}; const { selfStretch, flexSize } = layout; const hasChildLayout = selfStretch || flexSize; From 4935332ee1d09e6fe273e6b92e9da77d3a84fab5 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Thu, 2 Nov 2023 11:56:58 +0400 Subject: [PATCH 4/6] Use assignment --- packages/block-editor/src/hooks/layout.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/block-editor/src/hooks/layout.js b/packages/block-editor/src/hooks/layout.js index 6199d9f15bf8c7..67b170d22f036a 100644 --- a/packages/block-editor/src/hooks/layout.js +++ b/packages/block-editor/src/hooks/layout.js @@ -442,12 +442,12 @@ function BlockWithChildLayoutStyles( { block: BlockListBlock, props } ) { let css = ''; if ( selfStretch === 'fixed' && flexSize ) { - css += `${ selector } { + css = `${ selector } { flex-basis: ${ flexSize }; box-sizing: border-box; }`; } else if ( selfStretch === 'fill' ) { - css += `${ selector } { + css = `${ selector } { flex-grow: 1; }`; } From ab236358f82a5f15fda76f403ab9d43149a9d2d2 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Thu, 2 Nov 2023 12:13:24 +0400 Subject: [PATCH 5/6] Address selector feedback --- packages/block-editor/src/hooks/layout.js | 65 ++++++++++++++--------- 1 file changed, 40 insertions(+), 25 deletions(-) diff --git a/packages/block-editor/src/hooks/layout.js b/packages/block-editor/src/hooks/layout.js index 67b170d22f036a..959ab77f136978 100644 --- a/packages/block-editor/src/hooks/layout.js +++ b/packages/block-editor/src/hooks/layout.js @@ -347,11 +347,6 @@ export const withLayoutControls = createHigherOrderComponent( function BlockWithLayoutStyles( { block: BlockListBlock, props } ) { const { name, attributes } = props; - const disableLayoutStyles = useSelect( ( select ) => { - const { getSettings } = select( blockEditorStore ); - return !! getSettings().disableLayoutStyles; - } ); - const id = useInstanceId( BlockListBlock ); const { layout } = attributes; const { default: defaultBlockLayout } = @@ -369,22 +364,19 @@ function BlockWithLayoutStyles( { block: BlockListBlock, props } ) { // Get CSS string for the current layout type. // The CSS and `style` element is only output if it is not empty. - let css; - if ( ! disableLayoutStyles ) { - const fullLayoutType = getLayoutType( usedLayout?.type || 'default' ); - css = fullLayoutType?.getLayoutStyle?.( { - blockName: name, - selector, - layout: usedLayout, - style: attributes?.style, - hasBlockGapSupport, - } ); - } + const fullLayoutType = getLayoutType( usedLayout?.type || 'default' ); + const css = fullLayoutType?.getLayoutStyle?.( { + blockName: name, + selector, + layout: usedLayout, + style: attributes?.style, + hasBlockGapSupport, + } ); // Attach a `wp-container-` id-based class name as well as a layout class name such as `is-layout-flex`. const layoutClassNames = classnames( { - [ `wp-container-${ id }` ]: ! disableLayoutStyles && !! css, // Only attach a container class if there is generated CSS to be attached. + [ `wp-container-${ id }` ]: !! css, // Only attach a container class if there is generated CSS to be attached. }, layoutClasses ); @@ -418,7 +410,21 @@ function BlockWithLayoutStyles( { block: BlockListBlock, props } ) { */ export const withLayoutStyles = createHigherOrderComponent( ( BlockListBlock ) => ( props ) => { - if ( ! hasLayoutBlockSupport( props.name ) ) { + const blockSupportsLayout = hasLayoutBlockSupport( props.name ); + const shouldRenderLayoutStyles = useSelect( + ( select ) => { + // The callback returns early to avoid block editor subscription. + if ( ! blockSupportsLayout ) { + return false; + } + + return ! select( blockEditorStore ).getSettings() + .disableLayoutStyles; + }, + [ blockSupportsLayout ] + ); + + if ( ! shouldRenderLayoutStyles ) { return ; } @@ -432,10 +438,6 @@ export const withLayoutStyles = createHigherOrderComponent( function BlockWithChildLayoutStyles( { block: BlockListBlock, props } ) { const { style: { layout = {} } = {} } = props.attributes; const { selfStretch, flexSize } = layout; - const disableLayoutStyles = useSelect( ( select ) => { - const { getSettings } = select( blockEditorStore ); - return !! getSettings().disableLayoutStyles; - } ); const id = useInstanceId( BlockListBlock ); const selector = `.wp-container-content-${ id }`; @@ -453,8 +455,8 @@ function BlockWithChildLayoutStyles( { block: BlockListBlock, props } ) { } // Attach a `wp-container-content` id-based classname. - const className = classnames( props?.className, { - [ `wp-container-content-${ id }` ]: ! disableLayoutStyles && !! css, // Only attach a container class if there is generated CSS to be attached. + const className = classnames( props.className, { + [ `wp-container-content-${ id }` ]: !! css, // Only attach a container class if there is generated CSS to be attached. } ); const { setStyleOverride, deleteStyleOverride } = unlock( @@ -485,7 +487,20 @@ export const withChildLayoutStyles = createHigherOrderComponent( const { selfStretch, flexSize } = layout; const hasChildLayout = selfStretch || flexSize; - if ( ! hasChildLayout ) { + const shouldRenderChildLayoutStyles = useSelect( + ( select ) => { + // The callback returns early to avoid block editor subscription. + if ( ! hasChildLayout ) { + return false; + } + + return ! select( blockEditorStore ).getSettings() + .disableLayoutStyles; + }, + [ hasChildLayout ] + ); + + if ( ! shouldRenderChildLayoutStyles ) { return ; } From 9b88482625fa19e1541a19d2ce06800fa6bc37c6 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Fri, 3 Nov 2023 14:14:22 +0400 Subject: [PATCH 6/6] Feedback --- packages/block-editor/src/hooks/layout.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/block-editor/src/hooks/layout.js b/packages/block-editor/src/hooks/layout.js index 959ab77f136978..e6113484f8a2ce 100644 --- a/packages/block-editor/src/hooks/layout.js +++ b/packages/block-editor/src/hooks/layout.js @@ -436,7 +436,7 @@ export const withLayoutStyles = createHigherOrderComponent( ); function BlockWithChildLayoutStyles( { block: BlockListBlock, props } ) { - const { style: { layout = {} } = {} } = props.attributes; + const layout = props.attributes.style?.layout ?? {}; const { selfStretch, flexSize } = layout; const id = useInstanceId( BlockListBlock );