Skip to content

Commit

Permalink
Update theme.json class to support selectors API
Browse files Browse the repository at this point in the history
Leverage global block css selector function in theme.json
  • Loading branch information
aaronrobertshaw committed Mar 13, 2023
1 parent 3edc22b commit c7e307d
Showing 1 changed file with 191 additions and 84 deletions.
275 changes: 191 additions & 84 deletions lib/class-wp-theme-json-gutenberg.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,8 @@ class WP_Theme_JSON_Gutenberg {
* - prevent_override => Disables override of default presets by theme presets.
* The relationship between whether to override the defaults
* and whether the defaults are enabled is inverse:
* - If defaults are enabled => theme presets should not be overriden
* - If defaults are disabled => theme presets should be overriden
* - If defaults are enabled => theme presets should not be overridden
* - If defaults are disabled => theme presets should be overridden
* For example, a theme sets defaultPalette to false,
* making the default palette hidden from the user.
* In that case, we want all the theme presets to be present,
Expand Down Expand Up @@ -853,55 +853,21 @@ protected static function get_blocks_metadata() {
}

foreach ( $blocks as $block_name => $block_type ) {
if (
isset( $block_type->supports['__experimentalSelector'] ) &&
is_string( $block_type->supports['__experimentalSelector'] )
) {
static::$blocks_metadata[ $block_name ]['selector'] = $block_type->supports['__experimentalSelector'];
} else {
static::$blocks_metadata[ $block_name ]['selector'] = '.wp-block-' . str_replace( '/', '-', str_replace( 'core/', '', $block_name ) );
}
$root_selector = wp_get_block_css_selector( $block_type );

if (
isset( $block_type->supports['color']['__experimentalDuotone'] ) &&
is_string( $block_type->supports['color']['__experimentalDuotone'] )
) {
static::$blocks_metadata[ $block_name ]['duotone'] = $block_type->supports['color']['__experimentalDuotone'];
}

// Generate block support feature level selectors if opted into
// for the current block.
$features = array();
foreach ( static::BLOCK_SUPPORT_FEATURE_LEVEL_SELECTORS as $key => $feature ) {
if (
isset( $block_type->supports[ $key ]['__experimentalSelector'] ) &&
$block_type->supports[ $key ]['__experimentalSelector']
) {
$features[ $feature ] = static::scope_selector(
static::$blocks_metadata[ $block_name ]['selector'],
$block_type->supports[ $key ]['__experimentalSelector']
);
}
}
static::$blocks_metadata[ $block_name ]['selector'] = $root_selector;
static::$blocks_metadata[ $block_name ]['selectors'] = static::get_block_selectors( $block_type, $root_selector );

if ( ! empty( $features ) ) {
static::$blocks_metadata[ $block_name ]['features'] = $features;
$elements = static::get_block_element_selectors( $root_selector );
if ( ! empty( $elements ) ) {
static::$blocks_metadata[ $block_name ]['elements'] = $elements;
}

// Assign defaults, then overwrite those that the block sets by itself.
// If the block selector is compounded, will append the element to each
// individual block selector.
$block_selectors = explode( ',', static::$blocks_metadata[ $block_name ]['selector'] );
foreach ( static::ELEMENTS as $el_name => $el_selector ) {
$element_selector = array();
foreach ( $block_selectors as $selector ) {
if ( $selector === $el_selector ) {
$element_selector = array( $el_selector );
break;
}
$element_selector[] = static::append_to_selector( $el_selector, $selector . ' ', 'left' );
}
static::$blocks_metadata[ $block_name ]['elements'][ $el_name ] = implode( ',', $element_selector );
// The block may or may not have a duotone selector.
// TODO: Should this target be `color.duotone` not `duotone`?
$duotone_selector = wp_get_block_css_selector( $block_type, 'duotone' );
if ( null !== $duotone_selector ) {
static::$blocks_metadata[ $block_name ]['duotone'] = $duotone_selector;
}
// If the block has style variations, append their selectors to the block metadata.
if ( ! empty( $block_type->styles ) ) {
Expand Down Expand Up @@ -2232,8 +2198,8 @@ private static function get_block_nodes( $theme_json, $selectors = array() ) {
}

$feature_selectors = null;
if ( isset( $selectors[ $name ]['features'] ) ) {
$feature_selectors = $selectors[ $name ]['features'];
if ( isset( $selectors[ $name ]['selectors'] ) ) {
$feature_selectors = $selectors[ $name ]['selectors'];
}

$variation_selectors = array();
Expand All @@ -2250,8 +2216,8 @@ private static function get_block_nodes( $theme_json, $selectors = array() ) {
'name' => $name,
'path' => array( 'styles', 'blocks', $name ),
'selector' => $selector,
'selectors' => $feature_selectors,
'duotone' => $duotone_selector,
'features' => $feature_selectors,
'variations' => $variation_selectors,
);

Expand Down Expand Up @@ -2297,40 +2263,7 @@ public function get_styles_for_block( $block_metadata ) {
$selector = $block_metadata['selector'];
$settings = _wp_array_get( $this->theme_json, array( 'settings' ) );

/*
* Process style declarations for block support features the current
* block contains selectors for. Values for a feature with a custom
* selector are filtered from the theme.json node before it is
* processed as normal.
*/
$feature_declarations = array();

if ( ! empty( $block_metadata['features'] ) ) {
foreach ( $block_metadata['features'] as $feature_name => $feature_selector ) {
if ( ! empty( $node[ $feature_name ] ) ) {
// Create temporary node containing only the feature data
// to leverage existing `compute_style_properties` function.
$feature = array( $feature_name => $node[ $feature_name ] );
// Generate the feature's declarations only.
$new_feature_declarations = static::compute_style_properties( $feature, $settings, null, $this->theme_json );

// Merge new declarations with any that already exist for
// the feature selector. This may occur when multiple block
// support features use the same custom selector.
if ( isset( $feature_declarations[ $feature_selector ] ) ) {
foreach ( $new_feature_declarations as $new_feature_declaration ) {
$feature_declarations[ $feature_selector ][] = $new_feature_declaration;
}
} else {
$feature_declarations[ $feature_selector ] = $new_feature_declarations;
}

// Remove the feature from the block's node now the
// styles will be included under the feature level selector.
unset( $node[ $feature_name ] );
}
}
}
$feature_declarations = static::get_feature_declarations_for_block( $block_metadata, $node, $settings, $this->theme_json );

// If there are style variations, generate the declarations for them, including any feature selectors the block may have.
$style_variation_declarations = array();
Expand Down Expand Up @@ -3480,4 +3413,178 @@ public function set_spacing_sizes() {

_wp_array_set( $this->theme_json, array( 'settings', 'spacing', 'spacingSizes', 'default' ), $spacing_sizes );
}

/**
* Generates the root selector for a block.
*
* @param object $block_type The block type.
* @return string
*/
protected static function get_root_block_selector( $block_type ) {
// Prefer the selectors API if available.
if ( isset( $block_type->selectors ) &&
isset( $block_type->selectors['root'] )
) {
return $block_type->selectors['root'];
}

// Use the old experimental selector supports property if set.
if ( isset( $block_type->supports['__experimentalSelector'] ) &&
is_string( $block_type->supports['__experimentalSelector'] ) ) {
return $block_type->supports['__experimentalSelector'];
}

// Generate default block class selector.
$block_name = str_replace( '/', '-', str_replace( 'core/', '', $block_type->name ) );

return ".wp-block-{$block_name}";
}

/**
* Returns the selectors metadata for a block.
*
* @param object $block_type The block type.
* @param string $root_selector The block's root selector.
*
* @return object The custom selectors set by the block.
*/
protected static function get_block_selectors( $block_type, $root_selector ) {
if ( isset( $block_type->selectors ) ) {
return $block_type->selectors;
}

$selectors = array( 'root' => $root_selector );
foreach ( static::BLOCK_SUPPORT_FEATURE_LEVEL_SELECTORS as $key => $feature ) {
$feature_selector = wp_get_block_css_selector( $block_type, $key );
if ( null !== $feature_selector ) {
$selectors[ $feature ] = array( 'root' => $feature_selector );
}
}

return $selectors;
}

/**
* Generates all the element selectors for a block.
*
* @param string $root_selector The block's root CSS selector.
* @return array The block's element selectors.
*/
protected static function get_block_element_selectors( $root_selector ) {
// Assign defaults, then override those that the block sets by itself.
// If the block selector is compounded, will append the element to each
// individual block selector.
$block_selectors = explode( ',', $root_selector );
$element_selectors = array();

foreach ( static::ELEMENTS as $el_name => $el_selector ) {
$element_selector = array();
foreach ( $block_selectors as $selector ) {
if ( $selector === $el_selector ) {
$element_selector = array( $el_selector );
break;
}
$element_selector[] = static::append_to_selector( $el_selector, $selector . ' ', 'left' );
}
$element_selectors[ $el_name ] = implode( ',', $element_selector );
}

return $element_selectors;
}


/**
* Generates style declarations for the block's features e.g. color, border,
* typography etc, that have custom selectors in their block metadata.
*
* @param object $block_metadata The block's metadata containing selectors for
* features.
* @param object $block_node The merged theme.json node for the block.
* @param object $settings The theme.json settings for the node.
* @param object $theme_json The current theme.json config.
*
* @return array The style declarations for the block's features with custom
* selectors.
*/
protected static function get_feature_declarations_for_block( $block_metadata, &$block_node, $settings, $theme_json ) {
$declarations = array();

if ( ! isset( $block_metadata['selectors'] ) ) {
return $declarations;
}

foreach ( $block_metadata['selectors'] as $feature => $feature_selectors ) {
// Skip if this is the block's root selector or the block doesn't
// have any styles for the feature.
if ( 'root' === $feature || empty( $block_node[ $feature ] ) ) {
continue;
}

if ( is_array( $feature_selectors ) ) {
foreach ( $feature_selectors as $subfeature => $subfeature_selector ) {
if ( 'root' === $subfeature || empty( $block_node[ $feature ][ $subfeature ] ) ) {
continue;
}

// Create temporary node containing only the subfeature data
// to leverage existing `compute_style_properties` function.
$subfeature_node = array(
$feature => array(
$subfeature => $block_node[ $feature ][ $subfeature ],
),
);

// Generate style declarations.
$new_declarations = static::compute_style_properties( $subfeature_node, $settings, null, $theme_json );

// Merge subfeature declarations into feature declarations.
if ( isset( $declarations[ $subfeature_selector ] ) ) {
foreach ( $new_declarations as $new_declaration ) {
$declarations[ $subfeature_selector ][] = $new_declaration;
}
} else {
$declarations[ $subfeature_selector ] = $new_declarations;
}

// Remove the subfeature from the block's node now its
// styles will be included under its own selector not the
// block's.
unset( $block_node[ $feature ][ $subfeature ] );
}
}

// Now subfeatures have been processed and removed we can process
// feature root selector or simple string selector.
if (
is_string( $feature_selectors ) ||
( isset( $feature_selectors['root'] ) && $feature_selectors['root'] )
) {
$feature_selector = is_string( $feature_selectors ) ? $feature_selectors : $feature_selectors['root'];

// Create temporary node containing only the feature data
// to leverage existing `compute_style_properties` function.
$feature_node = array( $feature => $block_node[ $feature ] );

// Generate the style declarations.
$new_declarations = static::compute_style_properties( $feature_node, $settings, null, $theme_json );

// Merge new declarations with any that already exist for
// the feature selector. This may occur when multiple block
// support features use the same custom selector.
if ( isset( $declarations[ $feature_selector ] ) ) {
foreach ( $new_declarations as $new_declaration ) {
$declarations[ $feature_selector ][] = $new_declaration;
}
} else {
$declarations[ $feature_selector ] = $new_declarations;
}

// Remove the feature from the block's node now its styles
// will be included under its own selector not the block's.
unset( $block_node[ $feature ] );
}
}

return $declarations;
}
}

0 comments on commit c7e307d

Please sign in to comment.