Skip to content

Commit

Permalink
Add support for block variation styles
Browse files Browse the repository at this point in the history
  • Loading branch information
ntsekouras committed Apr 11, 2023
1 parent bae0d6b commit 42f6ad6
Show file tree
Hide file tree
Showing 9 changed files with 195 additions and 11 deletions.
14 changes: 14 additions & 0 deletions docs/reference-guides/data/data-core-blocks.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ _Parameters_

- _state_ `Object`: Data state.
- _name_ `string`: Block type name.
- _attributes_ `[Object]`: Block attributes. If provided, will try to find and include styles for specific block variations.

_Returns_

Expand Down Expand Up @@ -246,6 +247,19 @@ _Returns_

- `(WPBlockVariation[]|void)`: Block variations.

### getBlockVariationStyles

Returns block variation styles by block name.

_Parameters_

- _state_ `Object`: Data state.
- _name_ `string`: Block type name.

_Returns_

- `Array?`: Block Styles.

### getCategories

Returns all the available block categories.
Expand Down
41 changes: 41 additions & 0 deletions lib/compat/wordpress-6.3/script-loader.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,44 @@
remove_action( 'wp_body_open', 'wp_global_styles_render_svg_filters' );
remove_action( 'in_admin_header', 'wp_global_styles_render_svg_filters' );


/**
* Function responsible for enqueuing the assets required for block styles functionality on the editor.
*
* @since 5.3.0
* @since 6.3.0 Support styles for block variations.
*/
function gutenberg_enqueue_editor_block_styles_assets() {
$block_styles = WP_Block_Styles_Registry::get_instance()->get_all_registered();

$register_script_lines = array( '( function() {' );
foreach ( $block_styles as $block_name => $styles ) {
foreach ( $styles as $style_properties ) {
$block_style = array(
'name' => $style_properties['name'],
'label' => $style_properties['label'],
);
if ( isset( $style_properties['is_default'] ) ) {
$block_style['isDefault'] = $style_properties['is_default'];
}
if ( isset( $style_properties['variations'] ) ) {
$block_style['variations'] = $style_properties['variations'];
}
$register_script_lines[] = sprintf(
' wp.blocks.registerBlockStyle( \'%s\', %s );',
$block_name,
wp_json_encode( $block_style )
);
}
}
$register_script_lines[] = '} )();';
$inline_script = implode( "\n", $register_script_lines );

wp_register_script( 'wp-block-styles', false, array( 'wp-blocks' ), true, true );
wp_add_inline_script( 'wp-block-styles', $inline_script );
wp_enqueue_script( 'wp-block-styles' );
}

// Remove the Core action hook to avoid handling editor block style assets twice.
remove_action( 'enqueue_block_editor_assets', 'enqueue_editor_block_styles_assets' );
add_action( 'enqueue_block_editor_assets', 'gutenberg_enqueue_editor_block_styles_assets' );
10 changes: 7 additions & 3 deletions packages/block-editor/src/components/block-inspector/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -312,10 +312,14 @@ const BlockInspectorSingleBlock = ( { clientId, blockName } ) => {
const hasBlockStyles = useSelect(
( select ) => {
const { getBlockStyles } = select( blocksStore );
const blockStyles = getBlockStyles( blockName );
return blockStyles && blockStyles.length > 0;
const { getBlockAttributes } = select( blockEditorStore );
const blockStyles = getBlockStyles(
blockName,
getBlockAttributes( clientId )
);
return blockStyles?.length > 0;
},
[ blockName ]
[ clientId, blockName ]
);
const blockInformation = useBlockDisplayInformation( clientId );

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import { useEventHandlers } from './use-selected-block-event-handlers';
import { useNavModeExit } from './use-nav-mode-exit';
import { useBlockRefProvider } from './use-block-refs';
import { useIntersectionObserver } from './use-intersection-observer';
import useCleanBlockStyles from './use-clean-block-styles';
import { store as blockEditorStore } from '../../../store';
import useBlockOverlayActive from '../../block-content-overlay';

Expand Down Expand Up @@ -116,6 +117,9 @@ export function useBlockProps( props = {}, { __unstableIsHtml } = {} ) {
[ clientId ]
);

// This hook has side effects. Removes any lingering block variation styles.
useCleanBlockStyles( clientId );

const hasOverlay = useBlockOverlayActive( clientId );

// translators: %s: Type of block (i.e. Text, Image etc)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/**
* WordPress dependencies
*/
import { useEffect } from '@wordpress/element';
import { store as blocksStore } from '@wordpress/blocks';
import { useSelect, useDispatch } from '@wordpress/data';

/**
* Internal dependencies
*/
import { store as blockEditorStore } from '../../../store';

/**
* This hook checks if any applied block variation style is set,
* but the respective block variation is not active now.
*
* In this case this hook performs a side effect to remove
* that style(class name) and doesn't create an `undo` step.
*
* @param {string} clientId The block client ID.
*
*/
export default function useCleanBlockStyles( clientId ) {
const { updateBlockAttributes } = useDispatch( blockEditorStore );
const { __unstableMarkNextChangeAsNotPersistent } =
useDispatch( blockEditorStore );
const { styleToRemove, blockClassNames } = useSelect(
( select ) => {
const { getBlockAttributes, getBlockName } =
select( blockEditorStore );
const { getActiveBlockVariation, getBlockVariationStyles } =
select( blocksStore );
const attributes = getBlockAttributes( clientId );
if ( ! attributes.className ) {
return {};
}
// Here we are checking if the block has a block style for a specific
// block variation. If it does and the block variation is no longer active,
// we need to remove the style class name.
const blockName = getBlockName( clientId );
const match = getActiveBlockVariation( blockName, attributes );
const blockVariationStyles = getBlockVariationStyles( blockName );
return {
styleToRemove: blockVariationStyles?.find(
( { name: styleName, variations } ) =>
attributes.className.includes(
`is-style-${ styleName }`
) &&
( ! match || ! variations.includes( match.name ) )
)?.name,
blockClassNames: attributes.className,
};
},
[ clientId ]
);
useEffect( () => {
if ( styleToRemove === undefined ) {
return;
}
const updatedClassNames = blockClassNames
.split( ' ' )
.filter(
( blockClassName ) =>
! blockClassName.includes( `is-style-${ styleToRemove }` )
)
.join( ' ' );
__unstableMarkNextChangeAsNotPersistent();
updateBlockAttributes( clientId, {
className: updatedClassNames,
} );
}, [ clientId, styleToRemove, blockClassNames ] );
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export default function useStylesForBlocks( { clientId, onSwitch } ) {
return {
block,
blockType,
styles: getBlockStyles( block.name ),
styles: getBlockStyles( block.name, block.attributes ),
className: block.attributes.className || '',
};
};
Expand Down
7 changes: 5 additions & 2 deletions packages/block-editor/src/components/block-switcher/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,13 @@ export const BlockSwitcherDropdownMenu = ( { clientIds, blocks } ) => {
const rootClientId = getBlockRootClientId(
Array.isArray( clientIds ) ? clientIds[ 0 ] : clientIds
);
const [ { name: firstBlockName } ] = blocks;
const [
{ name: firstBlockName, attributes: firstBlockAttributes },
] = blocks;
const _isSingleBlockSelected = blocks.length === 1;
const styles =
_isSingleBlockSelected && getBlockStyles( firstBlockName );
_isSingleBlockSelected &&
getBlockStyles( firstBlockName, firstBlockAttributes );
let _icon;
if ( _isSingleBlockSelected ) {
_icon = blockInformation?.icon; // Take into account active block variations.
Expand Down
50 changes: 45 additions & 5 deletions packages/blocks/src/store/selectors.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,9 @@ export function getBlockType( state, name ) {
/**
* Returns block styles by block name.
*
* @param {Object} state Data state.
* @param {string} name Block type name.
* @param {Object} state Data state.
* @param {string} name Block type name.
* @param {Object} [attributes] Block attributes. If provided, will try to find and include styles for specific block variations.
*
* @example
* ```js
Expand All @@ -143,9 +144,48 @@ export function getBlockType( state, name ) {
*
* @return {Array?} Block Styles.
*/
export function getBlockStyles( state, name ) {
return state.blockStyles[ name ];
}
export const getBlockStyles = createSelector(
( state, name, attributes ) => {
const mainBlockStyles = state.blockStyles[ name ]?.filter(
( { variations } ) => ! variations
);
// If block attributes are provided, try to find styles for
// any active block variation.
if ( ! attributes ) {
return mainBlockStyles;
}
const activeVariation = getActiveBlockVariation(
state,
name,
attributes
);
if ( ! activeVariation ) {
return mainBlockStyles;
}
const activeVariationStyles = state.blockStyles[ name ]?.filter(
( { variations } ) => variations?.includes( activeVariation.name )
);
return [ ...activeVariationStyles, ...mainBlockStyles ];
},
( state, name ) => [ state.blockStyles[ name ] ]
);

/**
* Returns block variation styles by block name.
*
* @param {Object} state Data state.
* @param {string} name Block type name.
*
* @return {Array?} Block Styles.
*/
export const getBlockVariationStyles = createSelector(
( state, name ) => {
return state.blockStyles[ name ]?.filter(
( { variations } ) => !! variations
);
},
( state, name ) => [ state.blockStyles[ name ] ]
);

/**
* Returns block variations by block name.
Expand Down
6 changes: 6 additions & 0 deletions schemas/json/block.json
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,12 @@
"isDefault": {
"type": "boolean",
"default": false
},
"variations": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": [ "name", "label" ],
Expand Down

0 comments on commit 42f6ad6

Please sign in to comment.