-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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
Theme.json: Extend block style variations support #56540
Changes from all commits
a65a9f5
8da9d1f
01d1843
883c467
5f17722
a4d54d6
e5b5196
b66a1cb
4744dc3
1130551
d49e029
7582c5f
7426a3d
f48dd57
c47aa41
9aca87e
a003477
562a9fa
f09833f
29180f7
6413ecd
4f56178
f092ad7
15270b6
6fb1cfb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -841,9 +841,30 @@ protected static function sanitize( $input, $valid_block_names, $valid_element_n | |
|
||
$schema_styles_blocks = array(); | ||
$schema_settings_blocks = array(); | ||
|
||
// Generate a schema for blocks. | ||
// - Block styles can contain `elements` & `variations` definitions. | ||
// - Variations can contain styles for inner `blocks`. | ||
// - Variations definitions cannot be nested. | ||
// - Variations inner block styles cannot contain `elements`. | ||
// | ||
// As each variation needs a `blocks` schema but without `elements` and | ||
// inner `blocks`, the overall schema will be generated in multiple | ||
// passes. | ||
foreach ( $valid_block_names as $block ) { | ||
$schema_settings_blocks[ $block ] = static::VALID_SETTINGS; | ||
$schema_styles_blocks[ $block ] = $styles_non_top_level; | ||
} | ||
|
||
// Generate block style variations schema including nested block styles | ||
// schema as generated above. | ||
$block_style_variation_styles = static::VALID_STYLES; | ||
$block_style_variation_styles['blocks'] = $schema_styles_blocks; | ||
$block_style_variation_styles['elements'] = $schema_styles_elements; | ||
|
||
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'] ) && | ||
|
@@ -857,13 +878,14 @@ protected static function sanitize( $input, $valid_block_names, $valid_element_n | |
|
||
$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; | ||
$schema_styles_blocks[ $block ] = $styles_non_top_level; | ||
$schema_styles_blocks[ $block ]['elements'] = $schema_styles_elements; | ||
$schema_styles_blocks[ $block ]['variations'] = $schema_styles_variations; | ||
|
||
// The element styles schema can now be added for this block to the | ||
// styles.blocks.$block schema. | ||
$schema_styles_blocks[ $block ]['elements'] = $schema_styles_elements; | ||
} | ||
|
||
$schema['styles'] = static::VALID_STYLES; | ||
|
@@ -980,12 +1002,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'], | ||
$block_metadata['selector'] | ||
); | ||
} | ||
} | ||
|
||
static::$blocks_metadata[ $block_name ]['styleVariations'] = $style_selectors; | ||
} | ||
} | ||
|
||
return static::$blocks_metadata; | ||
} | ||
|
||
|
@@ -1009,7 +1053,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 ); | ||
} | ||
} | ||
|
||
|
@@ -1018,11 +1062,20 @@ 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 ) { | ||
$style_selectors[ $style['name'] ] = static::append_to_selector( '.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'], static::$blocks_metadata[ $block_name ]['selector'] ); | ||
} | ||
|
||
if ( ! empty( $style_selectors ) ) { | ||
static::$blocks_metadata[ $block_name ]['styleVariations'] = $style_selectors; | ||
} | ||
} | ||
|
@@ -1748,6 +1801,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 ); | ||
|
||
|
@@ -2380,38 +2437,80 @@ 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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm afraid the null coalescing operator isn't officially supported in WP yet. There's a ticket for it here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for flagging that. I had a suspicion that was the case although I recalled some recent performance PRs swapping out Do you think it is that much of a nuisance when it comes to backporting? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeahhh those perf improvements were changed before landing in core - see the discussion here. I'd avoid it if you don't want the backport PR to turn into an argument 😂 |
||
$duotone_selector = $selectors[ $name ]['duotone'] ?? null; | ||
$feature_selectors = $selectors[ $name ]['selectors'] ?? null; | ||
$variations = $node['variations'] ?? array(); | ||
$variation_selectors = array(); | ||
$variation_nodes = array(); | ||
|
||
$duotone_selector = null; | ||
if ( isset( $selectors[ $name ]['duotone'] ) ) { | ||
$duotone_selector = $selectors[ $name ]['duotone']; | ||
} | ||
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, | ||
); | ||
|
||
$feature_selectors = null; | ||
if ( isset( $selectors[ $name ]['selectors'] ) ) { | ||
$feature_selectors = $selectors[ $name ]['selectors']; | ||
} | ||
$variation_blocks = $variation_node['blocks'] ?? array(); | ||
$variation_elements = $variation_node['elements'] ?? array(); | ||
|
||
$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 ], | ||
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; | ||
|
||
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 ) { | ||
$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'] ) ) { | ||
|
@@ -2490,6 +2589,25 @@ 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; | ||
} | ||
|
||
$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' ); | ||
|
@@ -2591,6 +2709,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 => $individual_style_variation_block_declaration ) { | ||
$block_rules .= static::to_ruleset( $style_variation_block_selector, $individual_style_variation_block_declaration ); | ||
} | ||
|
||
return $block_rules; | ||
} | ||
|
||
|
@@ -3050,6 +3173,29 @@ public static function remove_insecure_properties( $theme_json ) { | |
} | ||
|
||
$variation_output = static::remove_insecure_styles( $variation_input ); | ||
|
||
// Process a variation's elements and element pseudo selector styles. | ||
aaronrobertshaw marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if ( isset( $variation_input['elements'] ) ) { | ||
foreach ( $valid_element_names as $element_name ) { | ||
$element_input = $variation_input['elements'][ $element_name ] ?? null; | ||
if ( $element_input ) { | ||
$element_output = static::remove_insecure_styles( $element_input ); | ||
|
||
if ( isset( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $element_name ] ) ) { | ||
foreach ( static::VALID_ELEMENT_PSEUDO_SELECTORS[ $element_name ] as $pseudo_selector ) { | ||
if ( isset( $element_input[ $pseudo_selector ] ) ) { | ||
$element_output[ $pseudo_selector ] = static::remove_insecure_styles( $element_input[ $pseudo_selector ] ); | ||
} | ||
} | ||
} | ||
|
||
if ( ! empty( $element_output ) ) { | ||
_wp_array_set( $variation_output, array( 'elements', $element_name ), $element_output ); | ||
} | ||
} | ||
} | ||
} | ||
|
||
if ( ! empty( $variation_output ) ) { | ||
_wp_array_set( $sanitized, $variation['path'], $variation_output ); | ||
} | ||
|
@@ -3229,6 +3375,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() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Block style variations will also now support registering them through the style registry with a style object. This function collects those style objects and returns them all as theme json data that can be merged into theme.json objects from other objects. See the changes to |
||
$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. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This tweak just allowed more flexible use of the
scope_selector
util function, simplifying code elsewhere.