Skip to content

Commit

Permalink
Update PHP processing of block style variations in theme.json
Browse files Browse the repository at this point in the history
Includes:
- absorbing block style variations from the block styles registry into theme.json
- updating metadata and stylesheet generation from variations to support inner block and element styles
  • Loading branch information
aaronrobertshaw committed Dec 14, 2023
1 parent d02898a commit 4c37968
Show file tree
Hide file tree
Showing 4 changed files with 538 additions and 97 deletions.
205 changes: 175 additions & 30 deletions lib/class-wp-theme-json-gutenberg.php
Original file line number Diff line number Diff line change
Expand Up @@ -842,6 +842,7 @@ protected static function sanitize( $input, $valid_block_names, $valid_element_n
foreach ( $valid_block_names as $block ) {
// Build the schema for each block style variation.
$style_variation_names = array();

if (
! empty( $input['styles']['blocks'][ $block ]['variations'] ) &&
is_array( $input['styles']['blocks'][ $block ]['variations'] ) &&
Expand All @@ -853,9 +854,14 @@ protected static function sanitize( $input, $valid_block_names, $valid_element_n
);
}

$schema_styles_variations = array();
$block_style_variation_styles = static::VALID_STYLES;
$block_style_variation_styles['blocks'] = null;
$block_style_variation_styles['elements'] = null;

$schema_styles_variations = array();
if ( ! empty( $style_variation_names ) ) {
$schema_styles_variations = array_fill_keys( $style_variation_names, $styles_non_top_level );
$schema_styles_variations = array_fill_keys( $style_variation_names, $block_style_variation_styles );
}

$schema_settings_blocks[ $block ] = static::VALID_SETTINGS;
Expand Down Expand Up @@ -978,12 +984,34 @@ protected static function prepend_to_selector( $selector, $to_prepend ) {
*/
protected static function get_blocks_metadata() {
// NOTE: the compat/6.1 version of this method in Gutenberg did not have these changes.
$registry = WP_Block_Type_Registry::get_instance();
$blocks = $registry->get_all_registered();
$registry = WP_Block_Type_Registry::get_instance();
$blocks = $registry->get_all_registered();
$style_registry = WP_Block_Styles_Registry::get_instance();

// Is there metadata for all currently registered blocks?
$blocks = array_diff_key( $blocks, static::$blocks_metadata );

if ( empty( $blocks ) ) {
// New block styles may have been registered within WP_Block_Styles_Registry.
// Update block metadata for any new block style variations.
$registered_styles = $style_registry->get_all_registered();
foreach ( static::$blocks_metadata as $block_name => $block_metadata ) {
if ( ! empty( $registered_styles[ $block_name ] ) ) {
$style_selectors = $block_metadata['styleVariations'] ?? array();

foreach ( $registered_styles[ $block_name ] as $block_style ) {
if ( ! isset( $style_selectors[ $block_style['name'] ] ) ) {
$style_selectors[ $block_style['name'] ] = static::append_to_selector(
'.is-style-' . $block_style['name'] . '.is-style-' . $block_style['name'],
$block_metadata['selector']
);
}
}

static::$blocks_metadata[ $block_name ]['styleVariations'] = $style_selectors;
}
}

return static::$blocks_metadata;
}

Expand All @@ -1007,7 +1035,7 @@ protected static function get_blocks_metadata() {

if ( $duotone_support ) {
$root_selector = wp_get_block_css_selector( $block_type );
$duotone_selector = WP_Theme_JSON_Gutenberg::scope_selector( $root_selector, $duotone_support );
$duotone_selector = static::scope_selector( $root_selector, $duotone_support );
}
}

Expand All @@ -1016,12 +1044,21 @@ protected static function get_blocks_metadata() {
}

// If the block has style variations, append their selectors to the block metadata.
$style_selectors = array();
if ( ! empty( $block_type->styles ) ) {
$style_selectors = array();
foreach ( $block_type->styles as $style ) {
// The style variation classname is duplicated in the selector to ensure that it overrides core block styles.
$style_selectors[ $style['name'] ] = static::append_to_selector( '.is-style-' . $style['name'] . '.is-style-' . $style['name'], static::$blocks_metadata[ $block_name ]['selector'] );
}
}

// Block style variations can be registered through the WP_Block_Styles_Registry as well as block.json.
$registered_styles = $style_registry->get_registered_styles_for_block( $block_name );
foreach ( $registered_styles as $style ) {
$style_selectors[ $style['name'] ] = static::append_to_selector( '.is-style-' . $style['name'] . '.is-style-' . $style['name'], static::$blocks_metadata[ $block_name ]['selector'] );
}

if ( ! empty( $style_selectors ) ) {
static::$blocks_metadata[ $block_name ]['styleVariations'] = $style_selectors;
}
}
Expand Down Expand Up @@ -1194,7 +1231,8 @@ public function get_stylesheet( $types = array( 'variables', 'styles', 'presets'
}

if ( in_array( 'styles', $types, true ) ) {
if ( false !== $root_style_key ) {
$skip_root_layout_styles = $options['skip_root_layout_styles'] ?? false;
if ( false !== $root_style_key && ! $skip_root_layout_styles ) {
$stylesheet .= $this->get_root_layout_rules( $style_nodes[ $root_style_key ]['selector'], $style_nodes[ $root_style_key ] );
}
$stylesheet .= $this->get_block_classes( $style_nodes );
Expand Down Expand Up @@ -1747,6 +1785,10 @@ protected static function compute_preset_classes( $settings, $selector, $origins
* @return string Scoped selector.
*/
public static function scope_selector( $scope, $selector ) {
if ( ! $selector || ! $scope ) {
return $selector;
}

$scopes = explode( ',', $scope );
$selectors = explode( ',', $selector );

Expand Down Expand Up @@ -2379,38 +2421,85 @@ private static function get_block_nodes( $theme_json, $selectors = array() ) {
}

foreach ( $theme_json['styles']['blocks'] as $name => $node ) {
$selector = null;
if ( isset( $selectors[ $name ]['selector'] ) ) {
$selector = $selectors[ $name ]['selector'];
}
$selector = $selectors[ $name ]['selector'] ?? null;
$duotone_selector = $selectors[ $name ]['duotone'] ?? null;
$feature_selectors = $selectors[ $name ]['selectors'] ?? null;
$variations = $node['variations'] ?? array();
$variation_selectors = array();
$variation_nodes = array();

// TODO: Should we be supporting recursive variations and block type styles?
foreach ( $variations as $variation => $variation_node ) {
$variation_selector = $selectors[ $name ]['styleVariations'][ $variation ];
$variation_selectors[] = array(
'path' => array( 'styles', 'blocks', $name, 'variations', $variation ),
'selector' => $variation_selector,
);

$duotone_selector = null;
if ( isset( $selectors[ $name ]['duotone'] ) ) {
$duotone_selector = $selectors[ $name ]['duotone'];
}
$variation_blocks = $variation_node['blocks'] ?? array();
$variation_elements = $variation_node['elements'] ?? array();

$feature_selectors = null;
if ( isset( $selectors[ $name ]['selectors'] ) ) {
$feature_selectors = $selectors[ $name ]['selectors'];
}
foreach ( $variation_blocks as $variation_block => $variation_block_node ) {
$variation_block_selector = static::scope_selector( $variation_selector, $selectors[ $variation_block ]['selector'] ?? null );
$variation_duotone_selector = static::scope_selector( $variation_selector, $selectors[ $variation_block ]['duotone'] ?? null );
$variation_feature_selectors = $selectors[ $variation_block ]['selectors'] ?? null;

$variation_selectors = array();
if ( isset( $node['variations'] ) ) {
foreach ( $node['variations'] as $variation => $node ) {
$variation_selectors[] = array(
'path' => array( 'styles', 'blocks', $name, 'variations', $variation ),
'selector' => $selectors[ $name ]['styleVariations'][ $variation ],
if ( $variation_feature_selectors ) {
foreach ( $variation_feature_selectors as $feature => $feature_selector ) {
if ( is_string( $feature_selector ) ) {
$variation_feature_selectors[ $feature ] = static::scope_selector( $variation_selector, $feature_selector );
}

if ( is_array( $feature_selector ) ) {
foreach ( $feature_selector as $subfeature => $subfeature_selector ) {
$variation_feature_selectors[ $feature ][ $subfeature ] = static::scope_selector( $variation_selector, $subfeature_selector );
}
}
}
}

$variation_nodes[] = array(
'name' => $variation_block,
'path' => array( 'styles', 'blocks', $name, 'variations', $variation, 'blocks', $variation_block ),
'selector' => $variation_block_selector,
'selectors' => $variation_feature_selectors,
'duotone' => $variation_duotone_selector,
);
}

foreach ( $variation_elements as $variation_element => $variation_element_node ) {
// TODO: See if there is a way to clean up the generation of element selectors.
// The following code varies from standard block element selectors only to avoid
// that the $selectors[ $name ]['elements'][ $variation_element ] value would
// nest the block's root selector.
$nodes[] = array(
'path' => array( 'styles', 'blocks', $name, 'variations', $variation, 'elements', $variation_element ),
'selector' => static::scope_selector( $variation_selector, static::ELEMENTS[ $variation_element ] ),
);

// Handle any pseudo selectors for the element.
if ( isset( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $variation_element ] ) ) {
foreach ( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $variation_element ] as $pseudo_selector ) {
if ( isset( $variation_element_node[ $pseudo_selector ] ) ) {
$pseudo_element_selector = static::append_to_selector( static::ELEMENTS[ $variation_element ], $pseudo_selector );
$nodes[] = array(
'path' => array( 'styles', 'blocks', $name, 'variations', $variation, 'elements', $variation_element ),
'selector' => static::scope_selector( $variation_selector, $pseudo_element_selector ),
);
}
}
}
}
}

$nodes[] = array(
'name' => $name,
'path' => array( 'styles', 'blocks', $name ),
'selector' => $selector,
'selectors' => $feature_selectors,
'duotone' => $duotone_selector,
'variations' => $variation_selectors,
'name' => $name,
'path' => array( 'styles', 'blocks', $name ),
'selector' => $selector,
'selectors' => $feature_selectors,
'duotone' => $duotone_selector,
'variations' => $variation_selectors,
'variation_nodes' => $variation_nodes,
);

if ( isset( $theme_json['styles']['blocks'][ $name ]['elements'] ) ) {
Expand Down Expand Up @@ -2489,6 +2578,28 @@ static function ( $split_selector ) use ( $clean_style_variation_selector ) {
}
}

$style_variation_block_declarations = array();
if ( ! empty( $block_metadata['variation_nodes'] ) ) {
foreach ( $block_metadata['variation_nodes'] as $variation_node ) {
$style_variation_block_node = _wp_array_get( $this->theme_json, $variation_node['path'], array() );
$variation_block_declarations = static::get_feature_declarations_for_node( $variation_node, $style_variation_block_node );

foreach ( $variation_block_declarations as $current_selector => $new_declarations ) {
$style_variation_block_declarations[ $current_selector ] = $new_declarations;
}

// Note that `get_feature_declarations_for_node` will also unset
// feature values so they aren't duplicated in declarations via
// the call below.
$style_variation_block_declarations[ $variation_node['selector'] ] = static::compute_style_properties(
$style_variation_block_node,
$settings,
null,
$this->theme_json
);
}
}

/*
* Get a reference to element name from path.
* $block_metadata['path'] = array( 'styles','elements','link' );
Expand Down Expand Up @@ -2590,6 +2701,11 @@ static function ( $pseudo_selector ) use ( $selector ) {
$block_rules .= static::to_ruleset( $style_variation_selector, $individual_style_variation_declarations );
}

// 7. Generate and append the block style variations for inner blocks and elements.
foreach ( $style_variation_block_declarations as $style_variation_block_selector => $indvidial_style_variation_block_declaration ) {
$block_rules .= static::to_ruleset( $style_variation_block_selector, $indvidial_style_variation_block_declaration );
}

return $block_rules;
}

Expand Down Expand Up @@ -3228,6 +3344,35 @@ public function get_raw_data() {
return $this->theme_json;
}

/**
* Converts block styles registered through the `WP_Block_Styles_Registry`
* with a style object, into theme.json format.
*
* @since 6.5.0
*
* @return array Styles configuration adhering to the theme.json schema.
*/
public static function get_from_block_styles_registry() {
$variations_data = array();
$registry = WP_Block_Styles_Registry::get_instance();
$styles = $registry->get_all_registered();

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

return array(
'version' => static::LATEST_SCHEMA,
'styles' => array(
'blocks' => $variations_data,
),
);
}

/**
* Transforms the given editor settings according the
* add_theme_support format to the theme.json format.
Expand Down
Loading

0 comments on commit 4c37968

Please sign in to comment.