Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Section Styles: improve performance and conceptual consistency #6873

Closed
209 changes: 8 additions & 201 deletions src/wp-includes/block-supports/block-style-variations.php
Original file line number Diff line number Diff line change
Expand Up @@ -212,176 +212,6 @@ function wp_render_block_style_variation_class_name( $block_content, $block ) {
return $tags->get_updated_html();
}

/**
* Collects block style variation data for merging with theme.json data.
*
* @since 6.6.0
* @access private
*
* @param array $variations Shared block style variations.
*
* @return array Block variations data to be merged under `styles.blocks`.
*/
function wp_resolve_block_style_variations( $variations ) {
$variations_data = array();

if ( empty( $variations ) ) {
return $variations_data;
}

$have_named_variations = ! wp_is_numeric_array( $variations );

foreach ( $variations as $key => $variation ) {
$supported_blocks = $variation['blockTypes'] ?? array();

/*
* Standalone theme.json partial files for block style variations
* will have their styles under a top-level property by the same name.
* Variations defined within an existing theme.json or theme style
* variation will themselves already be the required styles data.
*/
$variation_data = $variation['styles'] ?? $variation;

if ( empty( $variation_data ) ) {
continue;
}

/*
* Block style variations read in via standalone theme.json partials
* need to have their name set to the kebab case version of their title.
*/
$variation_name = $have_named_variations ? $key : ( $variation['slug'] ?? _wp_to_kebab_case( $variation['title'] ) );

foreach ( $supported_blocks as $block_type ) {
// Add block style variation data under current block type.
$path = array( $block_type, 'variations', $variation_name );
_wp_array_set( $variations_data, $path, $variation_data );
}
}

return $variations_data;
}

/**
* Merges variations data with existing theme.json data ensuring that the
* current theme.json data values take precedence.
*
* @since 6.6.0
* @access private
*
* @param array $variations_data Block style variations data keyed by block type.
* @param WP_Theme_JSON_Data $theme_json Current theme.json data.
* @param string $origin Origin for the theme.json data.
*
* @return WP_Theme_JSON The merged theme.json data.
*/
function wp_merge_block_style_variations_data( $variations_data, $theme_json, $origin = 'theme' ) {
if ( empty( $variations_data ) ) {
return $theme_json;
}

$variations_theme_json_data = array(
'version' => WP_Theme_JSON::LATEST_SCHEMA,
'styles' => array( 'blocks' => $variations_data ),
);

$variations_theme_json = new WP_Theme_JSON_Data( $variations_theme_json_data, $origin );

/*
* Merge the current theme.json data over shared variation data so that
* any explicit per block variation values take precedence.
*/
return $variations_theme_json->update_with( $theme_json->get_data() );
}

/**
* Merges any shared block style variation definitions from a theme style
* variation into their appropriate block type within theme json styles. Any
* custom user selections already made will take precedence over the shared
* style variation value.
*
* @since 6.6.0
* @access private
*
* @param WP_Theme_JSON_Data $theme_json Current theme.json data.
*
* @return WP_Theme_JSON_Data
*/
function wp_resolve_block_style_variations_from_theme_style_variation( $theme_json ) {
$theme_json_data = $theme_json->get_data();
$shared_variations = $theme_json_data['styles']['blocks']['variations'] ?? array();
$variations_data = wp_resolve_block_style_variations( $shared_variations );

return wp_merge_block_style_variations_data( $variations_data, $theme_json, 'user' );
}

/**
* Merges block style variation data sourced from standalone partial
* theme.json files.
*
* @since 6.6.0
* @access private
*
* @param WP_Theme_JSON_Data $theme_json Current theme.json data.
*
* @return WP_Theme_JSON_Data
*/
function wp_resolve_block_style_variations_from_theme_json_partials( $theme_json ) {
$block_style_variations = WP_Theme_JSON_Resolver::get_style_variations( 'block' );
$variations_data = wp_resolve_block_style_variations( $block_style_variations );

return wp_merge_block_style_variations_data( $variations_data, $theme_json );
}

/**
* Merges shared block style variations registered within the
* `styles.blocks.variations` property of the primary theme.json file.
*
* @since 6.6.0
* @access private
*
* @param WP_Theme_JSON_Data $theme_json Current theme.json data.
*
* @return WP_Theme_JSON_Data
*/
function wp_resolve_block_style_variations_from_primary_theme_json( $theme_json ) {
$theme_json_data = $theme_json->get_data();
$block_style_variations = $theme_json_data['styles']['blocks']['variations'] ?? array();
$variations_data = wp_resolve_block_style_variations( $block_style_variations );

return wp_merge_block_style_variations_data( $variations_data, $theme_json );
}

/**
* Merges block style variations registered via the block styles registry with a
* style object, under their appropriate block types within theme.json styles.
* Any variation values defined within the theme.json specific to a block type
* will take precedence over these shared definitions.
*
* @since 6.6.0
* @access private
*
* @param WP_Theme_JSON_Data $theme_json Current theme.json data.
*
* @return WP_Theme_JSON_Data
*/
function wp_resolve_block_style_variations_from_styles_registry( $theme_json ) {
$registry = WP_Block_Styles_Registry::get_instance();
$styles = $registry->get_all_registered();
$variations_data = array();

foreach ( $styles as $block_type => $variations ) {
foreach ( $variations as $variation_name => $variation ) {
if ( ! empty( $variation['style_data'] ) ) {
$path = array( $block_type, 'variations', $variation_name );
_wp_array_set( $variations_data, $path, $variation['style_data'] );
}
}
}

return wp_merge_block_style_variations_data( $variations_data, $theme_json );
}

/**
* Enqueues styles for block style variations.
*
Expand All @@ -399,53 +229,30 @@ function wp_enqueue_block_style_variation_styles() {
add_filter( 'render_block', 'wp_render_block_style_variation_class_name', 10, 2 );
add_action( 'wp_enqueue_scripts', 'wp_enqueue_block_style_variation_styles', 1 );

// Resolve block style variations from all their potential sources. The order here is deliberate.
add_filter( 'wp_theme_json_data_theme', 'wp_resolve_block_style_variations_from_primary_theme_json', 10, 1 );
add_filter( 'wp_theme_json_data_theme', 'wp_resolve_block_style_variations_from_theme_json_partials', 10, 1 );
add_filter( 'wp_theme_json_data_theme', 'wp_resolve_block_style_variations_from_styles_registry', 10, 1 );

add_filter( 'wp_theme_json_data_user', 'wp_resolve_block_style_variations_from_theme_style_variation', 10, 1 );

/**
* Registers any block style variations contained within the provided
* theme.json data.
* Registers block style variations read in from theme.json partials.
*
* @since 6.6.0
* @access private
*
* @param array $variations Shared block style variations.
*/
function wp_register_block_style_variations_from_theme_json_data( $variations ) {
function wp_register_block_style_variations_from_theme_json_partials( $variations ) {
if ( empty( $variations ) ) {
return $variations;
return;
}

$registry = WP_Block_Styles_Registry::get_instance();
$have_named_variations = ! wp_is_numeric_array( $variations );

foreach ( $variations as $key => $variation ) {
$supported_blocks = $variation['blockTypes'] ?? array();
$registry = WP_Block_Styles_Registry::get_instance();

/*
* Standalone theme.json partial files for block style variations
* will have their styles under a top-level property by the same name.
* Variations defined within an existing theme.json or theme style
* variation will themselves already be the required styles data.
*/
$variation_data = $variation['styles'] ?? $variation;

if ( empty( $variation_data ) ) {
foreach ( $variations as $variation ) {
if ( empty( $variation['blockTypes'] ) || empty( $variation['styles'] ) ) {
continue;
}

/*
* Block style variations read in via standalone theme.json partials
* need to have their name set to the kebab case version of their title.
*/
$variation_name = $have_named_variations ? $key : ( $variation['slug'] ?? _wp_to_kebab_case( $variation['title'] ) );
$variation_name = $variation['slug'] ?? _wp_to_kebab_case( $variation['title'] );
$variation_label = $variation['title'] ?? $variation_name;

foreach ( $supported_blocks as $block_type ) {
foreach ( $variation['blockTypes'] as $block_type ) {
$registered_styles = $registry->get_registered_styles_for_block( $block_type );

// Register block style variation if it hasn't already been registered.
Expand Down
117 changes: 105 additions & 12 deletions src/wp-includes/class-wp-theme-json-resolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ protected static function has_same_registered_blocks( $origin ) {
* @since 5.9.0 Theme supports have been inlined and the `$theme_support_data` argument removed.
* @since 6.0.0 Added an `$options` parameter to allow the theme data to be returned without theme supports.
* @since 6.6.0 Add support for 'default-font-sizes' and 'default-spacing-sizes' theme supports.
* Register the block style variations coming from the partials and the theme.json.
* Added registration and merging of block style variations from partial theme.json files and the block styles registry.
*
* @param array $deprecated Deprecated. Not used.
* @param array $options {
Expand All @@ -258,13 +258,29 @@ public static function get_theme_data( $deprecated = array(), $options = array()
$theme_json_data = array( 'version' => WP_Theme_JSON::LATEST_SCHEMA );
}

// Register variations defined by the theme.
$variations = $theme_json_data['styles']['blocks']['variations'] ?? array();
wp_register_block_style_variations_from_theme_json_data( $variations );

// Register variations defined by theme partials (theme.json files in the styles directory).
/*
* Register variations defined by theme partials (theme.json files in the styles directory).
* This is required so the variations pass sanitization of theme.json data.
*/
$variations = static::get_style_variations( 'block' );
wp_register_block_style_variations_from_theme_json_data( $variations );
wp_register_block_style_variations_from_theme_json_partials( $variations );

/*
* Source variations from the block registry and block style variation files. Then, merge them into the existing theme.json data.
*
* In case the same style properties are defined in several sources, this is how we should resolve the values,
* from higher to lower priority:
*
* - styles.blocks.blockType.variations from theme.json
* - styles.variations from theme.json
* - variations from block style variation files
* - variations from block styles registry
*
* See test_add_registered_block_styles_to_theme_data and test_unwraps_block_style_variations.
*
*/
$theme_json_data = static::inject_variations_from_block_style_variation_files( $theme_json_data, $variations );
$theme_json_data = static::inject_variations_from_block_styles_registry( $theme_json_data );

/**
* Filters the data provided by the theme for global styles and settings.
Expand Down Expand Up @@ -579,10 +595,6 @@ public static function get_user_data() {
unset( $decoded_data['isGlobalStylesUserThemeJSON'] );
$config = $decoded_data;
}

// Register variations defined by the user.
$variations = $config['styles']['blocks']['variations'] ?? array();
wp_register_block_style_variations_from_theme_json_data( $variations );
}

/** This filter is documented in wp-includes/class-wp-theme-json-resolver.php */
Expand Down Expand Up @@ -885,7 +897,7 @@ public static function get_resolved_theme_uris( $theme_json ) {
*
* @since 6.6.0
*
* @param WP_Theme_JSON $theme_json A theme json instance.
* @param WP_Theme_JSON $theme_json A theme json instance.
* @return WP_Theme_JSON Theme merged with resolved paths, if any found.
*/
public static function resolve_theme_file_uris( $theme_json ) {
Expand All @@ -907,4 +919,85 @@ public static function resolve_theme_file_uris( $theme_json ) {

return $theme_json;
}

/**
* Adds variations sourced from block style variations files to the supplied theme.json data.
*
* @since 6.6.0
*
* @param array $data Array following the theme.json specification.
* @param array $variations Shared block style variations.
* @return array Theme json data including shared block style variation definitions.
*/
private static function inject_variations_from_block_style_variation_files( $data, $variations ) {
if ( empty( $variations ) ) {
return $data;
}

foreach ( $variations as $variation ) {
if ( empty( $variation['styles'] ) || empty( $variation['blockTypes'] ) ) {
continue;
}

$variation_name = $variation['slug'] ?? _wp_to_kebab_case( $variation['title'] );

foreach ( $variation['blockTypes'] as $block_type ) {
// First, override partial styles with any top-level styles.
$top_level_data = $data['styles']['variations'][ $variation_name ] ?? array();
if ( ! empty( $top_level_data ) ) {
$variation['styles'] = array_replace_recursive( $variation['styles'], $top_level_data );
}

// Then, override styles so far with any block-level styles.
$block_level_data = $data['styles']['blocks'][ $block_type ]['variations'][ $variation_name ] ?? array();
if ( ! empty( $block_level_data ) ) {
$variation['styles'] = array_replace_recursive( $variation['styles'], $block_level_data );
}

$path = array( 'styles', 'blocks', $block_type, 'variations', $variation_name );
_wp_array_set( $data, $path, $variation['styles'] );
}
}

return $data;
}

/**
* Adds variations sourced from the block styles registry to the supplied theme.json data.
*
* @since 6.6.0
*
* @param array $data Array following the theme.json specification.
* @param array $variations Shared block style variations.
* @return array Theme json data including shared block style variation definitions.
*/
private static function inject_variations_from_block_styles_registry( $data ) {
$registry = WP_Block_Styles_Registry::get_instance();
$styles = $registry->get_all_registered();

foreach ( $styles as $block_type => $variations ) {
foreach ( $variations as $variation_name => $variation ) {
if ( empty( $variation['style_data'] ) ) {
continue;
}

// First, override registry styles with any top-level styles.
$top_level_data = $data['styles']['variations'][ $variation_name ] ?? array();
if ( ! empty( $top_level_data ) ) {
$variation['style_data'] = array_replace_recursive( $variation['style_data'], $top_level_data );
}

// Then, override styles so far with any block-level styles.
$block_level_data = $data['styles']['blocks'][ $block_type ]['variations'][ $variation_name ] ?? array();
if ( ! empty( $block_level_data ) ) {
$variation['style_data'] = array_replace_recursive( $variation['style_data'], $block_level_data );
}

$path = array( 'styles', 'blocks', $block_type, 'variations', $variation_name );
_wp_array_set( $data, $path, $variation['style_data'] );
}
}

return $data;
}
}
Loading
Loading