lol backups and old site first commit
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
/**
|
||||
* Align block support flag.
|
||||
*
|
||||
* @package WordPress
|
||||
* @since 5.6.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Registers the align block attribute for block types that support it.
|
||||
*
|
||||
* @since 5.6.0
|
||||
* @access private
|
||||
*
|
||||
* @param WP_Block_Type $block_type Block Type.
|
||||
*/
|
||||
function wp_register_alignment_support( $block_type ) {
|
||||
$has_align_support = block_has_support( $block_type, 'align', false );
|
||||
if ( $has_align_support ) {
|
||||
if ( ! $block_type->attributes ) {
|
||||
$block_type->attributes = array();
|
||||
}
|
||||
|
||||
if ( ! array_key_exists( 'align', $block_type->attributes ) ) {
|
||||
$block_type->attributes['align'] = array(
|
||||
'type' => 'string',
|
||||
'enum' => array( 'left', 'center', 'right', 'wide', 'full', '' ),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds CSS classes for block alignment to the incoming attributes array.
|
||||
* This will be applied to the block markup in the front-end.
|
||||
*
|
||||
* @since 5.6.0
|
||||
* @access private
|
||||
*
|
||||
* @param WP_Block_Type $block_type Block Type.
|
||||
* @param array $block_attributes Block attributes.
|
||||
* @return array Block alignment CSS classes and inline styles.
|
||||
*/
|
||||
function wp_apply_alignment_support( $block_type, $block_attributes ) {
|
||||
$attributes = array();
|
||||
$has_align_support = block_has_support( $block_type, 'align', false );
|
||||
if ( $has_align_support ) {
|
||||
$has_block_alignment = array_key_exists( 'align', $block_attributes );
|
||||
|
||||
if ( $has_block_alignment ) {
|
||||
$attributes['class'] = sprintf( 'align%s', $block_attributes['align'] );
|
||||
}
|
||||
}
|
||||
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
// Register the block support.
|
||||
WP_Block_Supports::get_instance()->register(
|
||||
'align',
|
||||
array(
|
||||
'register_attribute' => 'wp_register_alignment_support',
|
||||
'apply' => 'wp_apply_alignment_support',
|
||||
)
|
||||
);
|
||||
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
/**
|
||||
* Anchor block support flag.
|
||||
*
|
||||
* @package WordPress
|
||||
* @since 7.0.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Registers the anchor block attribute for block types that support it.
|
||||
*
|
||||
* @since 7.0.0
|
||||
* @access private
|
||||
*
|
||||
* @param WP_Block_Type $block_type Block Type.
|
||||
*/
|
||||
function wp_register_anchor_support( WP_Block_Type $block_type ) {
|
||||
if ( ! block_has_support( $block_type, array( 'anchor' ) ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! isset( $block_type->attributes ) ) {
|
||||
$block_type->attributes = array();
|
||||
}
|
||||
|
||||
if ( ! array_key_exists( 'anchor', $block_type->attributes ) ) {
|
||||
$block_type->attributes['anchor'] = array(
|
||||
'type' => 'string',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the anchor id to the output.
|
||||
*
|
||||
* @since 7.0.0
|
||||
* @access private
|
||||
*
|
||||
* @param WP_Block_Type $block_type Block Type.
|
||||
* @param array<string, mixed> $block_attributes Block attributes.
|
||||
* @return array<string, string> Attributes with block anchor id.
|
||||
*/
|
||||
function wp_apply_anchor_support( WP_Block_Type $block_type, array $block_attributes ): array {
|
||||
if ( empty( $block_attributes ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
if ( ! block_has_support( $block_type, array( 'anchor' ) ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
if ( ! isset( $block_attributes['anchor'] ) || ! is_string( $block_attributes['anchor'] ) || '' === $block_attributes['anchor'] ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
return array( 'id' => $block_attributes['anchor'] );
|
||||
}
|
||||
|
||||
// Register the block support.
|
||||
WP_Block_Supports::get_instance()->register(
|
||||
'anchor',
|
||||
array(
|
||||
'register_attribute' => 'wp_register_anchor_support',
|
||||
'apply' => 'wp_apply_anchor_support',
|
||||
)
|
||||
);
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
/**
|
||||
* Aria label block support flag.
|
||||
*
|
||||
* @package WordPress
|
||||
* @since 6.8.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Registers the aria-label block attribute for block types that support it.
|
||||
*
|
||||
* @since 6.8.0
|
||||
* @access private
|
||||
*
|
||||
* @param WP_Block_Type $block_type Block Type.
|
||||
*/
|
||||
function wp_register_aria_label_support( $block_type ) {
|
||||
$has_aria_label_support = block_has_support( $block_type, array( 'ariaLabel' ), false );
|
||||
|
||||
if ( ! $has_aria_label_support ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! $block_type->attributes ) {
|
||||
$block_type->attributes = array();
|
||||
}
|
||||
|
||||
if ( ! array_key_exists( 'ariaLabel', $block_type->attributes ) ) {
|
||||
$block_type->attributes['ariaLabel'] = array(
|
||||
'type' => 'string',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the aria-label to the output.
|
||||
*
|
||||
* @since 6.8.0
|
||||
* @access private
|
||||
*
|
||||
* @param WP_Block_Type $block_type Block Type.
|
||||
* @param array $block_attributes Block attributes.
|
||||
*
|
||||
* @return array Block aria-label.
|
||||
*/
|
||||
function wp_apply_aria_label_support( $block_type, $block_attributes ) {
|
||||
if ( ! $block_attributes ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$has_aria_label_support = block_has_support( $block_type, array( 'ariaLabel' ), false );
|
||||
if (
|
||||
! $has_aria_label_support ||
|
||||
wp_should_skip_block_supports_serialization( $block_type, 'ariaLabel' )
|
||||
) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$has_aria_label = array_key_exists( 'ariaLabel', $block_attributes );
|
||||
if ( ! $has_aria_label ) {
|
||||
return array();
|
||||
}
|
||||
return array( 'aria-label' => $block_attributes['ariaLabel'] );
|
||||
}
|
||||
|
||||
// Register the block support.
|
||||
WP_Block_Supports::get_instance()->register(
|
||||
'aria-label',
|
||||
array(
|
||||
'register_attribute' => 'wp_register_aria_label_support',
|
||||
'apply' => 'wp_apply_aria_label_support',
|
||||
)
|
||||
);
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
/**
|
||||
* Auto-register block support.
|
||||
*
|
||||
* @package WordPress
|
||||
* @since 7.0.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Marks user-defined attributes for auto-generated inspector controls.
|
||||
*
|
||||
* This filter runs during block type registration, before the WP_Block_Type
|
||||
* is instantiated. Block supports add their attributes AFTER the block type
|
||||
* is created (via {@see WP_Block_Supports::register_attributes()}), so any attributes
|
||||
* present at this stage are user-defined.
|
||||
*
|
||||
* The marker tells generateFieldsFromAttributes() which attributes should
|
||||
* get auto-generated inspector controls. Attributes are excluded if they:
|
||||
* - Have a 'source' (HTML-derived, edited inline not via inspector)
|
||||
* - Have role 'local' (internal state, not user-configurable)
|
||||
* - Have an unsupported type (only 'string', 'number', 'integer', 'boolean' are supported)
|
||||
* - Were added by block supports (added after this filter runs)
|
||||
*
|
||||
* @since 7.0.0
|
||||
* @access private
|
||||
*
|
||||
* @param array<string, mixed> $args Array of arguments for registering a block type.
|
||||
* @return array<string, mixed> Modified block type arguments.
|
||||
*/
|
||||
function wp_mark_auto_generate_control_attributes( array $args ): array {
|
||||
if ( empty( $args['attributes'] ) || ! is_array( $args['attributes'] ) ) {
|
||||
return $args;
|
||||
}
|
||||
|
||||
$has_auto_register = ! empty( $args['supports']['autoRegister'] );
|
||||
if ( ! $has_auto_register ) {
|
||||
return $args;
|
||||
}
|
||||
|
||||
foreach ( $args['attributes'] as $attr_key => $attr_schema ) {
|
||||
// Skip HTML-derived attributes (edited inline, not via inspector).
|
||||
if ( ! empty( $attr_schema['source'] ) ) {
|
||||
continue;
|
||||
}
|
||||
// Skip internal attributes (not user-configurable).
|
||||
if ( isset( $attr_schema['role'] ) && 'local' === $attr_schema['role'] ) {
|
||||
continue;
|
||||
}
|
||||
// Skip unsupported types (only 'string', 'number', 'integer', 'boolean' are supported).
|
||||
$type = $attr_schema['type'] ?? null;
|
||||
if ( ! in_array( $type, array( 'string', 'number', 'integer', 'boolean' ), true ) ) {
|
||||
continue;
|
||||
}
|
||||
$args['attributes'][ $attr_key ]['autoGenerateControl'] = true;
|
||||
}
|
||||
|
||||
return $args;
|
||||
}
|
||||
|
||||
// Priority 5 to mark original attributes before other filters (priority 10+) might add their own.
|
||||
add_filter( 'register_block_type_args', 'wp_mark_auto_generate_control_attributes', 5 );
|
||||
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
/**
|
||||
* Background block support flag.
|
||||
*
|
||||
* @package WordPress
|
||||
* @since 6.4.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Registers the style block attribute for block types that support it.
|
||||
*
|
||||
* @since 6.4.0
|
||||
* @access private
|
||||
*
|
||||
* @param WP_Block_Type $block_type Block Type.
|
||||
*/
|
||||
function wp_register_background_support( $block_type ) {
|
||||
// Setup attributes and styles within that if needed.
|
||||
if ( ! $block_type->attributes ) {
|
||||
$block_type->attributes = array();
|
||||
}
|
||||
|
||||
// Check for existing style attribute definition e.g. from block.json.
|
||||
if ( array_key_exists( 'style', $block_type->attributes ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$has_background_support = block_has_support( $block_type, array( 'background' ), false );
|
||||
|
||||
if ( $has_background_support ) {
|
||||
$block_type->attributes['style'] = array(
|
||||
'type' => 'object',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the background styles to the block wrapper.
|
||||
* This block support uses the `render_block` hook to ensure that
|
||||
* it is also applied to non-server-rendered blocks.
|
||||
*
|
||||
* @since 6.4.0
|
||||
* @since 6.5.0 Added support for `backgroundPosition` and `backgroundRepeat` output.
|
||||
* @since 6.6.0 Removed requirement for `backgroundImage.source`. A file/url is the default.
|
||||
* @since 6.7.0 Added support for `backgroundAttachment` output.
|
||||
* @since 7.1.0 Added support for `background.gradient` output.
|
||||
*
|
||||
* @access private
|
||||
*
|
||||
* @param string $block_content Rendered block content.
|
||||
* @param array $block Block object.
|
||||
* @return string Filtered block content.
|
||||
*/
|
||||
function wp_render_background_support( $block_content, $block ) {
|
||||
$block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] );
|
||||
$block_attributes = ( isset( $block['attrs'] ) && is_array( $block['attrs'] ) ) ? $block['attrs'] : array();
|
||||
$has_background_image_support = block_has_support( $block_type, array( 'background', 'backgroundImage' ), false );
|
||||
$has_background_gradient_support = block_has_support( $block_type, array( 'background', 'gradient' ), false );
|
||||
|
||||
if (
|
||||
( ! $has_background_image_support && ! $has_background_gradient_support ) ||
|
||||
! isset( $block_attributes['style']['background'] )
|
||||
) {
|
||||
return $block_content;
|
||||
}
|
||||
|
||||
// Check serialization skip for each feature individually.
|
||||
$skip_background_image = ! $has_background_image_support || wp_should_skip_block_supports_serialization( $block_type, 'background', 'backgroundImage' );
|
||||
$skip_background_gradient = ! $has_background_gradient_support || wp_should_skip_block_supports_serialization( $block_type, 'background', 'gradient' );
|
||||
|
||||
if ( $skip_background_image && $skip_background_gradient ) {
|
||||
return $block_content;
|
||||
}
|
||||
|
||||
$background_styles = array();
|
||||
|
||||
if ( ! $skip_background_image ) {
|
||||
$background_styles['backgroundImage'] = $block_attributes['style']['background']['backgroundImage'] ?? null;
|
||||
$background_styles['backgroundSize'] = $block_attributes['style']['background']['backgroundSize'] ?? null;
|
||||
$background_styles['backgroundPosition'] = $block_attributes['style']['background']['backgroundPosition'] ?? null;
|
||||
$background_styles['backgroundRepeat'] = $block_attributes['style']['background']['backgroundRepeat'] ?? null;
|
||||
$background_styles['backgroundAttachment'] = $block_attributes['style']['background']['backgroundAttachment'] ?? null;
|
||||
|
||||
if ( ! empty( $background_styles['backgroundImage'] ) ) {
|
||||
$background_styles['backgroundSize'] = $background_styles['backgroundSize'] ?? 'cover';
|
||||
|
||||
// If the background size is set to `contain` and no position is set, set the position to `center`.
|
||||
if ( 'contain' === $background_styles['backgroundSize'] && ! $background_styles['backgroundPosition'] ) {
|
||||
$background_styles['backgroundPosition'] = '50% 50%';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $skip_background_gradient ) {
|
||||
$background_styles['gradient'] = $block_attributes['style']['background']['gradient'] ?? null;
|
||||
}
|
||||
|
||||
$styles = wp_style_engine_get_styles( array( 'background' => $background_styles ) );
|
||||
|
||||
if ( ! empty( $styles['css'] ) ) {
|
||||
// Inject background styles to the first element, presuming it's the wrapper, if it exists.
|
||||
$tags = new WP_HTML_Tag_Processor( $block_content );
|
||||
|
||||
if ( $tags->next_tag() ) {
|
||||
$existing_style = $tags->get_attribute( 'style' );
|
||||
if ( is_string( $existing_style ) && '' !== $existing_style ) {
|
||||
$separator = str_ends_with( $existing_style, ';' ) ? '' : ';';
|
||||
$updated_style = "{$existing_style}{$separator}{$styles['css']}";
|
||||
} else {
|
||||
$updated_style = $styles['css'];
|
||||
}
|
||||
|
||||
$tags->set_attribute( 'style', $updated_style );
|
||||
$tags->add_class( 'has-background' );
|
||||
}
|
||||
|
||||
return $tags->get_updated_html();
|
||||
}
|
||||
|
||||
return $block_content;
|
||||
}
|
||||
|
||||
// Register the block support.
|
||||
WP_Block_Supports::get_instance()->register(
|
||||
'background',
|
||||
array(
|
||||
'register_attribute' => 'wp_register_background_support',
|
||||
)
|
||||
);
|
||||
|
||||
add_filter( 'render_block', 'wp_render_background_support', 10, 2 );
|
||||
@@ -0,0 +1,310 @@
|
||||
<?php
|
||||
/**
|
||||
* Block support to enable per-section styling of block types via
|
||||
* block style variations.
|
||||
*
|
||||
* @package WordPress
|
||||
* @since 6.6.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Determines the block style variation names within a CSS class string.
|
||||
*
|
||||
* @since 6.6.0
|
||||
*
|
||||
* @param string $class_string CSS class string to look for a variation in.
|
||||
*
|
||||
* @return array|null The block style variation name if found.
|
||||
*/
|
||||
function wp_get_block_style_variation_name_from_class( $class_string ) {
|
||||
if ( ! is_string( $class_string ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
preg_match_all( '/\bis-style-(?!default)(\S+)\b/', $class_string, $matches );
|
||||
return $matches[1] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively resolves any `ref` values within a block style variation's data.
|
||||
*
|
||||
* @since 6.6.0
|
||||
* @access private
|
||||
*
|
||||
* @param array $variation_data Reference to the variation data being processed.
|
||||
* @param array $theme_json Theme.json data to retrieve referenced values from.
|
||||
*/
|
||||
function wp_resolve_block_style_variation_ref_values( &$variation_data, $theme_json ) {
|
||||
foreach ( $variation_data as $key => &$value ) {
|
||||
// Only need to potentially process arrays.
|
||||
if ( is_array( $value ) ) {
|
||||
// If ref value is set, attempt to find its matching value and update it.
|
||||
if ( array_key_exists( 'ref', $value ) ) {
|
||||
// Clean up any invalid ref value.
|
||||
if ( empty( $value['ref'] ) || ! is_string( $value['ref'] ) ) {
|
||||
unset( $variation_data[ $key ] );
|
||||
}
|
||||
|
||||
$value_path = explode( '.', $value['ref'] ?? '' );
|
||||
$ref_value = _wp_array_get( $theme_json, $value_path );
|
||||
|
||||
// Only update the current value if the referenced path matched a value.
|
||||
if ( null === $ref_value ) {
|
||||
unset( $variation_data[ $key ] );
|
||||
} else {
|
||||
$value = $ref_value;
|
||||
}
|
||||
} else {
|
||||
// Recursively look for ref instances.
|
||||
wp_resolve_block_style_variation_ref_values( $value, $theme_json );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Renders the block style variation's styles.
|
||||
*
|
||||
* In the case of nested blocks with variations applied, we want the parent
|
||||
* variation's styles to be rendered before their descendants. This solves the
|
||||
* issue of a block type being styled in both the parent and descendant: we want
|
||||
* the descendant style to take priority, and this is done by loading it after,
|
||||
* in the DOM order. This is why the variation stylesheet generation is in a
|
||||
* different filter.
|
||||
*
|
||||
* @since 6.6.0
|
||||
* @access private
|
||||
*
|
||||
* @param array $parsed_block The parsed block.
|
||||
*
|
||||
* @return array The parsed block with block style variation classname added.
|
||||
*/
|
||||
function wp_render_block_style_variation_support_styles( $parsed_block ) {
|
||||
$classes = $parsed_block['attrs']['className'] ?? null;
|
||||
$variations = wp_get_block_style_variation_name_from_class( $classes );
|
||||
|
||||
if ( ! $variations ) {
|
||||
return $parsed_block;
|
||||
}
|
||||
|
||||
$tree = WP_Theme_JSON_Resolver::get_merged_data();
|
||||
$theme_json = $tree->get_raw_data();
|
||||
|
||||
// Only the first block style variation with data is supported.
|
||||
$variation_data = array();
|
||||
foreach ( $variations as $variation ) {
|
||||
$variation_data = $theme_json['styles']['blocks'][ $parsed_block['blockName'] ]['variations'][ $variation ] ?? array();
|
||||
|
||||
if ( ! empty( $variation_data ) ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( empty( $variation_data ) ) {
|
||||
return $parsed_block;
|
||||
}
|
||||
|
||||
/*
|
||||
* Recursively resolve any ref values with the appropriate value within the
|
||||
* theme_json data.
|
||||
*/
|
||||
wp_resolve_block_style_variation_ref_values( $variation_data, $theme_json );
|
||||
|
||||
$variation_instance = wp_unique_id( $variation . '--' );
|
||||
$class_name = "is-style-$variation_instance";
|
||||
$updated_class_name = $parsed_block['attrs']['className'] . " $class_name";
|
||||
|
||||
/*
|
||||
* Even though block style variations are effectively theme.json partials,
|
||||
* they can't be processed completely as though they are.
|
||||
*
|
||||
* Block styles support custom selectors to direct specific types of styles
|
||||
* to inner elements. For example, borders on Image block's get applied to
|
||||
* the inner `img` element rather than the wrapping `figure`.
|
||||
*
|
||||
* The following relocates the "root" block style variation styles to
|
||||
* under an appropriate blocks property to leverage the preexisting style
|
||||
* generation for simple block style variations. This way they get the
|
||||
* custom selectors they need.
|
||||
*
|
||||
* The inner elements and block styles for the variation itself are
|
||||
* still included at the top level but scoped by the variation's selector
|
||||
* when the stylesheet is generated.
|
||||
*/
|
||||
$elements_data = $variation_data['elements'] ?? array();
|
||||
$blocks_data = $variation_data['blocks'] ?? array();
|
||||
unset( $variation_data['elements'] );
|
||||
unset( $variation_data['blocks'] );
|
||||
|
||||
_wp_array_set(
|
||||
$blocks_data,
|
||||
array( $parsed_block['blockName'], 'variations', $variation_instance ),
|
||||
$variation_data
|
||||
);
|
||||
|
||||
$config = array(
|
||||
'version' => WP_Theme_JSON::LATEST_SCHEMA,
|
||||
'settings' => array(
|
||||
'spacing' => array(
|
||||
'blockGap' => true,
|
||||
),
|
||||
),
|
||||
'styles' => array(
|
||||
'elements' => $elements_data,
|
||||
'blocks' => $blocks_data,
|
||||
),
|
||||
);
|
||||
|
||||
// Ensure variation state styles know about any custom viewport breakpoints.
|
||||
if ( isset( $theme_json['settings']['viewport'] ) ) {
|
||||
$config['settings']['viewport'] = $theme_json['settings']['viewport'];
|
||||
}
|
||||
|
||||
// Turn off filter that excludes block nodes. They are needed here for the variation's inner block types.
|
||||
if ( ! is_admin() ) {
|
||||
remove_filter( 'wp_theme_json_get_style_nodes', 'wp_filter_out_block_nodes' );
|
||||
}
|
||||
|
||||
// Temporarily prevent variation instance from being sanitized while processing theme.json.
|
||||
$styles_registry = WP_Block_Styles_Registry::get_instance();
|
||||
$styles_registry->register( $parsed_block['blockName'], array( 'name' => $variation_instance ) );
|
||||
|
||||
$variation_theme_json = new WP_Theme_JSON( $config, 'blocks' );
|
||||
$variation_styles = $variation_theme_json->get_stylesheet(
|
||||
array( 'styles' ),
|
||||
array( 'custom' ),
|
||||
array(
|
||||
'include_block_style_variations' => true,
|
||||
'skip_root_layout_styles' => true,
|
||||
'scope' => ".$class_name",
|
||||
)
|
||||
);
|
||||
|
||||
// Clean up temporary block style now instance styles have been processed.
|
||||
$styles_registry->unregister( $parsed_block['blockName'], $variation_instance );
|
||||
|
||||
// Restore filter that excludes block nodes.
|
||||
if ( ! is_admin() ) {
|
||||
add_filter( 'wp_theme_json_get_style_nodes', 'wp_filter_out_block_nodes' );
|
||||
}
|
||||
|
||||
if ( empty( $variation_styles ) ) {
|
||||
return $parsed_block;
|
||||
}
|
||||
|
||||
wp_register_style( 'block-style-variation-styles', false, array( 'wp-block-library', 'global-styles' ) );
|
||||
wp_add_inline_style( 'block-style-variation-styles', $variation_styles );
|
||||
|
||||
/*
|
||||
* Add variation instance class name to block's className string so it can
|
||||
* be enforced in the block markup via render_block filter.
|
||||
*/
|
||||
_wp_array_set( $parsed_block, array( 'attrs', 'className' ), $updated_class_name );
|
||||
|
||||
return $parsed_block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures the variation block support class name generated and added to
|
||||
* block attributes in the `render_block_data` filter gets applied to the
|
||||
* block's markup.
|
||||
*
|
||||
* @since 6.6.0
|
||||
* @access private
|
||||
*
|
||||
* @see wp_render_block_style_variation_support_styles
|
||||
*
|
||||
* @param string $block_content Rendered block content.
|
||||
* @param array $block Block object.
|
||||
*
|
||||
* @return string Filtered block content.
|
||||
*/
|
||||
function wp_render_block_style_variation_class_name( $block_content, $block ) {
|
||||
if ( ! $block_content || empty( $block['attrs']['className'] ) ) {
|
||||
return $block_content;
|
||||
}
|
||||
|
||||
$block_class_name = $block['attrs']['className'];
|
||||
if ( ! is_string( $block_class_name ) ) {
|
||||
return $block_content;
|
||||
}
|
||||
|
||||
/*
|
||||
* Matches a class prefixed by `is-style`, followed by the
|
||||
* variation slug, then `--`, and finally an instance number.
|
||||
*/
|
||||
preg_match( '/\bis-style-(\S+?--\d+)\b/', $block_class_name, $matches );
|
||||
|
||||
if ( empty( $matches ) ) {
|
||||
return $block_content;
|
||||
}
|
||||
|
||||
$tags = new WP_HTML_Tag_Processor( $block_content );
|
||||
|
||||
if ( $tags->next_tag() ) {
|
||||
/*
|
||||
* Ensure the variation instance class name set in the
|
||||
* `render_block_data` filter is applied in markup.
|
||||
* See `wp_render_block_style_variation_support_styles`.
|
||||
*/
|
||||
$tags->add_class( $matches[0] );
|
||||
}
|
||||
|
||||
return $tags->get_updated_html();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues styles for block style variations.
|
||||
*
|
||||
* @since 6.6.0
|
||||
* @access private
|
||||
*/
|
||||
function wp_enqueue_block_style_variation_styles() {
|
||||
wp_enqueue_style( 'block-style-variation-styles' );
|
||||
}
|
||||
|
||||
// Register the block support.
|
||||
WP_Block_Supports::get_instance()->register( 'block-style-variation', array() );
|
||||
|
||||
add_filter( 'render_block_data', 'wp_render_block_style_variation_support_styles', 10, 2 );
|
||||
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 );
|
||||
|
||||
/**
|
||||
* 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_partials( $variations ) {
|
||||
if ( empty( $variations ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$registry = WP_Block_Styles_Registry::get_instance();
|
||||
|
||||
foreach ( $variations as $variation ) {
|
||||
if ( empty( $variation['blockTypes'] ) || empty( $variation['styles'] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$variation_name = $variation['slug'] ?? _wp_to_kebab_case( $variation['title'] );
|
||||
$variation_label = $variation['title'] ?? $variation_name;
|
||||
|
||||
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.
|
||||
if ( ! array_key_exists( $variation_name, $registered_styles ) ) {
|
||||
register_block_style(
|
||||
$block_type,
|
||||
array(
|
||||
'name' => $variation_name,
|
||||
'label' => $variation_label,
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
/**
|
||||
* Block visibility block support flag.
|
||||
*
|
||||
* @package WordPress
|
||||
* @since 6.9.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Render nothing if the block is hidden, or add viewport visibility styles.
|
||||
*
|
||||
* @since 6.9.0
|
||||
* @since 7.0.0 Added support for viewport visibility.
|
||||
* @access private
|
||||
*
|
||||
* @param string $block_content Rendered block content.
|
||||
* @param array $block Block object.
|
||||
* @return string Filtered block content.
|
||||
*/
|
||||
function wp_render_block_visibility_support( $block_content, $block ) {
|
||||
$block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] );
|
||||
|
||||
if ( ! $block_type ) {
|
||||
return $block_content;
|
||||
}
|
||||
|
||||
$block_visibility = $block['attrs']['metadata']['blockVisibility'] ?? null;
|
||||
|
||||
// Hide the block whenever the value is boolean false, regardless of the
|
||||
// block's current visibility support. This prevents blocks that previously
|
||||
// supported visibility from unintentionally appearing on the front end
|
||||
// after their support was disabled.
|
||||
if ( false === $block_visibility ) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if ( ! block_has_support( $block_type, 'visibility', true ) ) {
|
||||
return $block_content;
|
||||
}
|
||||
|
||||
if ( is_array( $block_visibility ) && ! empty( $block_visibility ) ) {
|
||||
$viewport_config = $block_visibility['viewport'] ?? null;
|
||||
|
||||
if ( ! is_array( $viewport_config ) || empty( $viewport_config ) ) {
|
||||
return $block_content;
|
||||
}
|
||||
$viewport_settings = wp_get_global_settings( array( 'viewport' ) );
|
||||
$viewport_media_queries = WP_Theme_JSON::get_viewport_media_queries(
|
||||
$viewport_settings,
|
||||
array(
|
||||
'include_desktop' => true,
|
||||
)
|
||||
);
|
||||
|
||||
/*
|
||||
* Viewport media queries are keyed by style-state names (`@mobile`,
|
||||
* `@tablet`, and `@desktop`). Block visibility metadata and generated
|
||||
* classes use plain viewport names, so map the keys at this boundary.
|
||||
*/
|
||||
$block_visibility_media_queries = array();
|
||||
foreach ( $viewport_media_queries as $viewport_state => $media_query ) {
|
||||
$block_visibility_media_queries[ ltrim( $viewport_state, '@' ) ] = $media_query;
|
||||
}
|
||||
$viewport_media_queries = $block_visibility_media_queries;
|
||||
|
||||
$hidden_on = array();
|
||||
|
||||
// Collect which viewport the block is hidden on (only known viewport sizes).
|
||||
foreach ( $viewport_config as $viewport_config_size => $is_visible ) {
|
||||
if ( false === $is_visible && isset( $viewport_media_queries[ $viewport_config_size ] ) ) {
|
||||
$hidden_on[] = $viewport_config_size;
|
||||
}
|
||||
}
|
||||
|
||||
// If no viewport sizes have visibility set to false, return unchanged.
|
||||
if ( empty( $hidden_on ) ) {
|
||||
return $block_content;
|
||||
}
|
||||
|
||||
// Maintain consistent order of viewport sizes for class name generation.
|
||||
sort( $hidden_on );
|
||||
|
||||
$css_rules = array();
|
||||
$class_names = array();
|
||||
|
||||
foreach ( $hidden_on as $hidden_viewport_size ) {
|
||||
/*
|
||||
* If these values ever become user-defined,
|
||||
* they should be sanitized and kebab-cased.
|
||||
*/
|
||||
$visibility_class = 'wp-block-hidden-' . $hidden_viewport_size;
|
||||
$class_names[] = $visibility_class;
|
||||
$css_rules[] = array(
|
||||
'selector' => '.' . $visibility_class,
|
||||
'declarations' => array(
|
||||
'display' => 'none !important',
|
||||
),
|
||||
'rules_group' => $viewport_media_queries[ $hidden_viewport_size ],
|
||||
);
|
||||
}
|
||||
|
||||
wp_style_engine_get_stylesheet_from_css_rules(
|
||||
$css_rules,
|
||||
array(
|
||||
'context' => 'block-supports',
|
||||
'prettify' => false,
|
||||
)
|
||||
);
|
||||
|
||||
if ( ! empty( $block_content ) ) {
|
||||
$processor = new WP_HTML_Tag_Processor( $block_content );
|
||||
if ( $processor->next_tag() ) {
|
||||
$processor->add_class( implode( ' ', $class_names ) );
|
||||
|
||||
/*
|
||||
* Set all IMG tags to be `fetchpriority=auto` so that wp_get_loading_optimization_attributes() won't add
|
||||
* `fetchpriority=high` or increment the media count to affect whether subsequent IMG tags get `loading=lazy`.
|
||||
*/
|
||||
do {
|
||||
if ( 'IMG' === $processor->get_tag() ) {
|
||||
$processor->set_attribute( 'fetchpriority', 'auto' );
|
||||
}
|
||||
} while ( $processor->next_tag() );
|
||||
$block_content = $processor->get_updated_html();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $block_content;
|
||||
}
|
||||
|
||||
add_filter( 'render_block', 'wp_render_block_visibility_support', 10, 2 );
|
||||
@@ -0,0 +1,174 @@
|
||||
<?php
|
||||
/**
|
||||
* Border block support flag.
|
||||
*
|
||||
* @package WordPress
|
||||
* @since 5.8.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Registers the style attribute used by the border feature if needed for block
|
||||
* types that support borders.
|
||||
*
|
||||
* @since 5.8.0
|
||||
* @since 6.1.0 Improved conditional blocks optimization.
|
||||
* @access private
|
||||
*
|
||||
* @param WP_Block_Type $block_type Block Type.
|
||||
*/
|
||||
function wp_register_border_support( $block_type ) {
|
||||
// Setup attributes and styles within that if needed.
|
||||
if ( ! $block_type->attributes ) {
|
||||
$block_type->attributes = array();
|
||||
}
|
||||
|
||||
if ( block_has_support( $block_type, '__experimentalBorder' ) && ! array_key_exists( 'style', $block_type->attributes ) ) {
|
||||
$block_type->attributes['style'] = array(
|
||||
'type' => 'object',
|
||||
);
|
||||
}
|
||||
|
||||
if ( wp_has_border_feature_support( $block_type, 'color' ) && ! array_key_exists( 'borderColor', $block_type->attributes ) ) {
|
||||
$block_type->attributes['borderColor'] = array(
|
||||
'type' => 'string',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds CSS classes and inline styles for border styles to the incoming
|
||||
* attributes array. This will be applied to the block markup in the front-end.
|
||||
*
|
||||
* @since 5.8.0
|
||||
* @since 6.1.0 Implemented the style engine to generate CSS and classnames.
|
||||
* @access private
|
||||
*
|
||||
* @param WP_Block_Type $block_type Block type.
|
||||
* @param array $block_attributes Block attributes.
|
||||
* @return array Border CSS classes and inline styles.
|
||||
*/
|
||||
function wp_apply_border_support( $block_type, $block_attributes ) {
|
||||
if ( wp_should_skip_block_supports_serialization( $block_type, 'border' ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$border_block_styles = array();
|
||||
$has_border_color_support = wp_has_border_feature_support( $block_type, 'color' );
|
||||
$has_border_width_support = wp_has_border_feature_support( $block_type, 'width' );
|
||||
|
||||
// Border radius.
|
||||
if (
|
||||
wp_has_border_feature_support( $block_type, 'radius' ) &&
|
||||
isset( $block_attributes['style']['border']['radius'] ) &&
|
||||
! wp_should_skip_block_supports_serialization( $block_type, '__experimentalBorder', 'radius' )
|
||||
) {
|
||||
$border_radius = $block_attributes['style']['border']['radius'];
|
||||
|
||||
if ( is_numeric( $border_radius ) ) {
|
||||
$border_radius .= 'px';
|
||||
}
|
||||
|
||||
$border_block_styles['radius'] = $border_radius;
|
||||
}
|
||||
|
||||
// Border style.
|
||||
if (
|
||||
wp_has_border_feature_support( $block_type, 'style' ) &&
|
||||
isset( $block_attributes['style']['border']['style'] ) &&
|
||||
! wp_should_skip_block_supports_serialization( $block_type, '__experimentalBorder', 'style' )
|
||||
) {
|
||||
$border_block_styles['style'] = $block_attributes['style']['border']['style'];
|
||||
}
|
||||
|
||||
// Border width.
|
||||
if (
|
||||
$has_border_width_support &&
|
||||
isset( $block_attributes['style']['border']['width'] ) &&
|
||||
! wp_should_skip_block_supports_serialization( $block_type, '__experimentalBorder', 'width' )
|
||||
) {
|
||||
$border_width = $block_attributes['style']['border']['width'];
|
||||
|
||||
// This check handles original unitless implementation.
|
||||
if ( is_numeric( $border_width ) ) {
|
||||
$border_width .= 'px';
|
||||
}
|
||||
|
||||
$border_block_styles['width'] = $border_width;
|
||||
}
|
||||
|
||||
// Border color.
|
||||
if (
|
||||
$has_border_color_support &&
|
||||
! wp_should_skip_block_supports_serialization( $block_type, '__experimentalBorder', 'color' )
|
||||
) {
|
||||
$preset_border_color = array_key_exists( 'borderColor', $block_attributes ) ? "var:preset|color|{$block_attributes['borderColor']}" : null;
|
||||
$custom_border_color = $block_attributes['style']['border']['color'] ?? null;
|
||||
$border_block_styles['color'] = $preset_border_color ? $preset_border_color : $custom_border_color;
|
||||
}
|
||||
|
||||
// Generates styles for individual border sides.
|
||||
if ( $has_border_color_support || $has_border_width_support ) {
|
||||
foreach ( array( 'top', 'right', 'bottom', 'left' ) as $side ) {
|
||||
$border = $block_attributes['style']['border'][ $side ] ?? null;
|
||||
$border_side_values = array(
|
||||
'width' => isset( $border['width'] ) && ! wp_should_skip_block_supports_serialization( $block_type, '__experimentalBorder', 'width' ) ? $border['width'] : null,
|
||||
'color' => isset( $border['color'] ) && ! wp_should_skip_block_supports_serialization( $block_type, '__experimentalBorder', 'color' ) ? $border['color'] : null,
|
||||
'style' => isset( $border['style'] ) && ! wp_should_skip_block_supports_serialization( $block_type, '__experimentalBorder', 'style' ) ? $border['style'] : null,
|
||||
);
|
||||
$border_block_styles[ $side ] = $border_side_values;
|
||||
}
|
||||
}
|
||||
|
||||
// Collect classes and styles.
|
||||
$attributes = array();
|
||||
$styles = wp_style_engine_get_styles( array( 'border' => $border_block_styles ) );
|
||||
|
||||
if ( ! empty( $styles['classnames'] ) ) {
|
||||
$attributes['class'] = $styles['classnames'];
|
||||
}
|
||||
|
||||
if ( ! empty( $styles['css'] ) ) {
|
||||
$attributes['style'] = $styles['css'];
|
||||
}
|
||||
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the current block type supports the border feature requested.
|
||||
*
|
||||
* If the `__experimentalBorder` support flag is a boolean `true` all border
|
||||
* support features are available. Otherwise, the specific feature's support
|
||||
* flag nested under `experimentalBorder` must be enabled for the feature
|
||||
* to be opted into.
|
||||
*
|
||||
* @since 5.8.0
|
||||
* @access private
|
||||
*
|
||||
* @param WP_Block_Type $block_type Block type to check for support.
|
||||
* @param string $feature Name of the feature to check support for.
|
||||
* @param mixed $default_value Fallback value for feature support, defaults to false.
|
||||
* @return bool Whether the feature is supported.
|
||||
*/
|
||||
function wp_has_border_feature_support( $block_type, $feature, $default_value = false ) {
|
||||
// Check if all border support features have been opted into via `"__experimentalBorder": true`.
|
||||
if ( $block_type instanceof WP_Block_Type ) {
|
||||
$block_type_supports_border = $block_type->supports['__experimentalBorder'] ?? $default_value;
|
||||
if ( true === $block_type_supports_border ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the specific feature has been opted into individually
|
||||
// via nested flag under `__experimentalBorder`.
|
||||
return block_has_support( $block_type, array( '__experimentalBorder', $feature ), $default_value );
|
||||
}
|
||||
|
||||
// Register the block support.
|
||||
WP_Block_Supports::get_instance()->register(
|
||||
'border',
|
||||
array(
|
||||
'register_attribute' => 'wp_register_border_support',
|
||||
'apply' => 'wp_apply_border_support',
|
||||
)
|
||||
);
|
||||
@@ -0,0 +1,155 @@
|
||||
<?php
|
||||
/**
|
||||
* Colors block support flag.
|
||||
*
|
||||
* @package WordPress
|
||||
* @since 5.6.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Registers the style and colors block attributes for block types that support it.
|
||||
*
|
||||
* @since 5.6.0
|
||||
* @since 6.1.0 Improved $color_support assignment optimization.
|
||||
* @access private
|
||||
*
|
||||
* @param WP_Block_Type $block_type Block Type.
|
||||
*/
|
||||
function wp_register_colors_support( $block_type ) {
|
||||
$color_support = false;
|
||||
if ( $block_type instanceof WP_Block_Type ) {
|
||||
$color_support = $block_type->supports['color'] ?? false;
|
||||
}
|
||||
$has_text_colors_support = true === $color_support ||
|
||||
( isset( $color_support['text'] ) && $color_support['text'] ) ||
|
||||
( is_array( $color_support ) && ! isset( $color_support['text'] ) );
|
||||
$has_background_colors_support = true === $color_support ||
|
||||
( isset( $color_support['background'] ) && $color_support['background'] ) ||
|
||||
( is_array( $color_support ) && ! isset( $color_support['background'] ) );
|
||||
$has_gradients_support = $color_support['gradients'] ?? false;
|
||||
$has_link_colors_support = $color_support['link'] ?? false;
|
||||
$has_button_colors_support = $color_support['button'] ?? false;
|
||||
$has_heading_colors_support = $color_support['heading'] ?? false;
|
||||
$has_color_support = $has_text_colors_support ||
|
||||
$has_background_colors_support ||
|
||||
$has_gradients_support ||
|
||||
$has_link_colors_support ||
|
||||
$has_button_colors_support ||
|
||||
$has_heading_colors_support;
|
||||
|
||||
if ( ! $block_type->attributes ) {
|
||||
$block_type->attributes = array();
|
||||
}
|
||||
|
||||
if ( $has_color_support && ! array_key_exists( 'style', $block_type->attributes ) ) {
|
||||
$block_type->attributes['style'] = array(
|
||||
'type' => 'object',
|
||||
);
|
||||
}
|
||||
|
||||
if ( $has_background_colors_support && ! array_key_exists( 'backgroundColor', $block_type->attributes ) ) {
|
||||
$block_type->attributes['backgroundColor'] = array(
|
||||
'type' => 'string',
|
||||
);
|
||||
}
|
||||
|
||||
if ( $has_text_colors_support && ! array_key_exists( 'textColor', $block_type->attributes ) ) {
|
||||
$block_type->attributes['textColor'] = array(
|
||||
'type' => 'string',
|
||||
);
|
||||
}
|
||||
|
||||
if ( $has_gradients_support && ! array_key_exists( 'gradient', $block_type->attributes ) ) {
|
||||
$block_type->attributes['gradient'] = array(
|
||||
'type' => 'string',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds CSS classes and inline styles for colors to the incoming attributes array.
|
||||
* This will be applied to the block markup in the front-end.
|
||||
*
|
||||
* @since 5.6.0
|
||||
* @since 6.1.0 Implemented the style engine to generate CSS and classnames.
|
||||
* @since 7.1.0 Suppresses color.gradient when background.gradient is supported and set.
|
||||
* @access private
|
||||
*
|
||||
* @param WP_Block_Type $block_type Block type.
|
||||
* @param array $block_attributes Block attributes.
|
||||
*
|
||||
* @return array Colors CSS classes and inline styles.
|
||||
*/
|
||||
function wp_apply_colors_support( $block_type, $block_attributes ) {
|
||||
$color_support = $block_type->supports['color'] ?? false;
|
||||
|
||||
if (
|
||||
is_array( $color_support ) &&
|
||||
wp_should_skip_block_supports_serialization( $block_type, 'color' )
|
||||
) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$has_text_colors_support = true === $color_support ||
|
||||
( isset( $color_support['text'] ) && $color_support['text'] ) ||
|
||||
( is_array( $color_support ) && ! isset( $color_support['text'] ) );
|
||||
$has_background_colors_support = true === $color_support ||
|
||||
( isset( $color_support['background'] ) && $color_support['background'] ) ||
|
||||
( is_array( $color_support ) && ! isset( $color_support['background'] ) );
|
||||
$has_gradients_support = $color_support['gradients'] ?? false;
|
||||
$color_block_styles = array();
|
||||
|
||||
// Text colors.
|
||||
if ( $has_text_colors_support && ! wp_should_skip_block_supports_serialization( $block_type, 'color', 'text' ) ) {
|
||||
$preset_text_color = array_key_exists( 'textColor', $block_attributes ) ? "var:preset|color|{$block_attributes['textColor']}" : null;
|
||||
$custom_text_color = $block_attributes['style']['color']['text'] ?? null;
|
||||
$color_block_styles['text'] = $preset_text_color ? $preset_text_color : $custom_text_color;
|
||||
}
|
||||
|
||||
// Background colors.
|
||||
if ( $has_background_colors_support && ! wp_should_skip_block_supports_serialization( $block_type, 'color', 'background' ) ) {
|
||||
$preset_background_color = array_key_exists( 'backgroundColor', $block_attributes ) ? "var:preset|color|{$block_attributes['backgroundColor']}" : null;
|
||||
$custom_background_color = $block_attributes['style']['color']['background'] ?? null;
|
||||
$color_block_styles['background'] = $preset_background_color ? $preset_background_color : $custom_background_color;
|
||||
}
|
||||
|
||||
// Gradients.
|
||||
// Suppress color.gradient CSS when background.gradient is supported and
|
||||
// explicitly set. background.php owns CSS generation in that case, and
|
||||
// emitting the background shorthand here would conflict with it.
|
||||
$has_background_gradient_support = block_has_support( $block_type, array( 'background', 'gradient' ), false );
|
||||
$has_background_gradient_value = ! empty( $block_attributes['style']['background']['gradient'] );
|
||||
|
||||
if (
|
||||
$has_gradients_support &&
|
||||
! wp_should_skip_block_supports_serialization( $block_type, 'color', 'gradients' ) &&
|
||||
! ( $has_background_gradient_support && $has_background_gradient_value )
|
||||
) {
|
||||
$preset_gradient_color = array_key_exists( 'gradient', $block_attributes ) ? "var:preset|gradient|{$block_attributes['gradient']}" : null;
|
||||
$custom_gradient_color = $block_attributes['style']['color']['gradient'] ?? null;
|
||||
$color_block_styles['gradient'] = $preset_gradient_color ? $preset_gradient_color : $custom_gradient_color;
|
||||
}
|
||||
|
||||
$attributes = array();
|
||||
$styles = wp_style_engine_get_styles( array( 'color' => $color_block_styles ), array( 'convert_vars_to_classnames' => true ) );
|
||||
|
||||
if ( ! empty( $styles['classnames'] ) ) {
|
||||
$attributes['class'] = $styles['classnames'];
|
||||
}
|
||||
|
||||
if ( ! empty( $styles['css'] ) ) {
|
||||
$attributes['style'] = $styles['css'];
|
||||
}
|
||||
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
// Register the block support.
|
||||
WP_Block_Supports::get_instance()->register(
|
||||
'colors',
|
||||
array(
|
||||
'register_attribute' => 'wp_register_colors_support',
|
||||
'apply' => 'wp_apply_colors_support',
|
||||
)
|
||||
);
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
/**
|
||||
* Custom classname block support flag.
|
||||
*
|
||||
* @package WordPress
|
||||
* @since 5.6.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Registers the custom classname block attribute for block types that support it.
|
||||
*
|
||||
* @since 5.6.0
|
||||
* @access private
|
||||
*
|
||||
* @param WP_Block_Type $block_type Block Type.
|
||||
*/
|
||||
function wp_register_custom_classname_support( $block_type ) {
|
||||
$has_custom_classname_support = block_has_support( $block_type, 'customClassName', true );
|
||||
|
||||
if ( $has_custom_classname_support ) {
|
||||
if ( ! $block_type->attributes ) {
|
||||
$block_type->attributes = array();
|
||||
}
|
||||
|
||||
if ( ! array_key_exists( 'className', $block_type->attributes ) ) {
|
||||
$block_type->attributes['className'] = array(
|
||||
'type' => 'string',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the custom classnames to the output.
|
||||
*
|
||||
* @since 5.6.0
|
||||
* @access private
|
||||
*
|
||||
* @param WP_Block_Type $block_type Block Type.
|
||||
* @param array $block_attributes Block attributes.
|
||||
*
|
||||
* @return array Block CSS classes and inline styles.
|
||||
*/
|
||||
function wp_apply_custom_classname_support( $block_type, $block_attributes ) {
|
||||
$has_custom_classname_support = block_has_support( $block_type, 'customClassName', true );
|
||||
$attributes = array();
|
||||
if ( $has_custom_classname_support ) {
|
||||
$has_custom_classnames = array_key_exists( 'className', $block_attributes );
|
||||
|
||||
if ( $has_custom_classnames ) {
|
||||
$attributes['class'] = $block_attributes['className'];
|
||||
}
|
||||
}
|
||||
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
// Register the block support.
|
||||
WP_Block_Supports::get_instance()->register(
|
||||
'custom-classname',
|
||||
array(
|
||||
'register_attribute' => 'wp_register_custom_classname_support',
|
||||
'apply' => 'wp_apply_custom_classname_support',
|
||||
)
|
||||
);
|
||||
@@ -0,0 +1,340 @@
|
||||
<?php
|
||||
/**
|
||||
* Custom CSS block support.
|
||||
*
|
||||
* @package WordPress
|
||||
*/
|
||||
|
||||
/**
|
||||
* Render the custom CSS stylesheet and add class name to block as required.
|
||||
*
|
||||
* @since 7.0.0
|
||||
*
|
||||
* @param array $parsed_block The parsed block.
|
||||
* @return array The same parsed block with custom CSS class name added if appropriate.
|
||||
*
|
||||
* @phpstan-param array{
|
||||
* blockName: string|null,
|
||||
* attrs: array{
|
||||
* className?: string,
|
||||
* style?: array{
|
||||
* css?: string,
|
||||
* ...
|
||||
* },
|
||||
* ...
|
||||
* },
|
||||
* ...
|
||||
* } $parsed_block
|
||||
* @phpstan-return array{
|
||||
* blockName: string|null,
|
||||
* attrs: array{
|
||||
* className?: string,
|
||||
* style?: array{
|
||||
* css?: string,
|
||||
* ...
|
||||
* },
|
||||
* ...
|
||||
* },
|
||||
* ...
|
||||
* }
|
||||
*/
|
||||
function wp_render_custom_css_support_styles( $parsed_block ) {
|
||||
$custom_css = $parsed_block['attrs']['style']['css'] ?? null;
|
||||
if ( ! is_string( $custom_css ) || '' === trim( $custom_css ) ) {
|
||||
return $parsed_block;
|
||||
}
|
||||
|
||||
$block_type = WP_Block_Type_Registry::get_instance()->get_registered( $parsed_block['blockName'] );
|
||||
if ( ! block_has_support( $block_type, 'customCSS', true ) ) {
|
||||
return $parsed_block;
|
||||
}
|
||||
|
||||
// Validate CSS doesn't contain HTML markup (same validation as global styles REST API).
|
||||
if ( preg_match( '#</?\w+#', $custom_css ) ) {
|
||||
return $parsed_block;
|
||||
}
|
||||
|
||||
// Generate a unique class name for this block instance.
|
||||
$class_name = wp_unique_id_from_values( $parsed_block, 'wp-custom-css-' );
|
||||
$existing_class_name = $parsed_block['attrs']['className'] ?? null;
|
||||
$updated_class_name = is_string( $existing_class_name )
|
||||
? "$existing_class_name $class_name"
|
||||
: $class_name;
|
||||
|
||||
$parsed_block['attrs']['className'] = $updated_class_name;
|
||||
|
||||
// Process the custom CSS using the same method as global styles.
|
||||
$selector = '.' . $class_name;
|
||||
$processed_css = WP_Theme_JSON::process_blocks_custom_css( $custom_css, $selector );
|
||||
|
||||
if ( ! empty( $processed_css ) ) {
|
||||
/**
|
||||
* Reuse one handle so identical custom CSS is enqueued only once via
|
||||
* {@see wp_unique_id_from_values()}. Explicitly declare the `wp-block-library`
|
||||
* dependency so `global-styles` is guaranteed to print after it, preventing
|
||||
* block default styles from unintentionally overriding global styles.
|
||||
*/
|
||||
$handle = 'wp-block-custom-css';
|
||||
if ( ! wp_style_is( $handle, 'registered' ) ) {
|
||||
wp_register_style( $handle, false, array( 'wp-block-library', 'global-styles' ) );
|
||||
}
|
||||
$after_styles = wp_styles()->get_data( $handle, 'after' );
|
||||
if ( ! is_array( $after_styles ) ) {
|
||||
$after_styles = array();
|
||||
}
|
||||
if ( ! in_array( $processed_css, $after_styles, true ) ) {
|
||||
wp_add_inline_style( $handle, $processed_css );
|
||||
}
|
||||
}
|
||||
|
||||
return $parsed_block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues the block custom CSS styles.
|
||||
*
|
||||
* @since 7.0.0
|
||||
*/
|
||||
function wp_enqueue_block_custom_css() {
|
||||
wp_enqueue_style( 'wp-block-custom-css' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the custom CSS class name to the block's rendered HTML.
|
||||
*
|
||||
* The class name is generated in {@see wp_render_custom_css_support_styles()}
|
||||
* and stored in block attributes. This filter adds it to the actual markup.
|
||||
*
|
||||
* @since 7.0.0
|
||||
*
|
||||
* @param string $block_content Rendered block content.
|
||||
* @param array $block Block object.
|
||||
* @return string Filtered block content.
|
||||
*
|
||||
* @phpstan-param array{
|
||||
* attrs: array{
|
||||
* className?: string,
|
||||
* ...
|
||||
* },
|
||||
* ...
|
||||
* } $block
|
||||
*/
|
||||
function wp_render_custom_css_class_name( $block_content, $block ) {
|
||||
$class_name_attr = $block['attrs']['className'] ?? null;
|
||||
$class_name_prefix = 'wp-custom-css-';
|
||||
if ( ! is_string( $class_name_attr ) || ! str_contains( $class_name_attr, $class_name_prefix ) ) {
|
||||
return $block_content;
|
||||
}
|
||||
|
||||
// Parse out the 'wp-custom-css-*' class name added by wp_render_custom_css_support_styles().
|
||||
$matched_class_name = null;
|
||||
$token_delimiter = " \t\f\r\n";
|
||||
$class_token = strtok( $class_name_attr, $token_delimiter );
|
||||
while ( false !== $class_token ) {
|
||||
if ( str_starts_with( $class_token, $class_name_prefix ) ) {
|
||||
$matched_class_name = $class_token;
|
||||
break;
|
||||
}
|
||||
$class_token = strtok( $token_delimiter );
|
||||
}
|
||||
if ( null === $matched_class_name ) {
|
||||
return $block_content;
|
||||
}
|
||||
|
||||
$tags = new WP_HTML_Tag_Processor( $block_content );
|
||||
if ( $tags->next_tag() ) {
|
||||
$tags->add_class( 'has-custom-css' );
|
||||
$tags->add_class( $matched_class_name );
|
||||
}
|
||||
|
||||
return $tags->get_updated_html();
|
||||
}
|
||||
|
||||
add_filter( 'render_block', 'wp_render_custom_css_class_name', 10, 2 );
|
||||
add_filter( 'render_block_data', 'wp_render_custom_css_support_styles', 10, 1 );
|
||||
add_action( 'wp_enqueue_scripts', 'wp_enqueue_block_custom_css', 1 );
|
||||
|
||||
/**
|
||||
* Registers the style block attribute for block types that support it.
|
||||
*
|
||||
* @param WP_Block_Type $block_type Block Type.
|
||||
*/
|
||||
function wp_register_custom_css_support( $block_type ) {
|
||||
// Setup attributes and styles within that if needed.
|
||||
if ( ! $block_type->attributes ) {
|
||||
$block_type->attributes = array();
|
||||
}
|
||||
|
||||
// Check for existing style attribute definition e.g. from block.json.
|
||||
if ( array_key_exists( 'style', $block_type->attributes ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$has_custom_css_support = block_has_support( $block_type, array( 'customCSS' ), true );
|
||||
|
||||
if ( $has_custom_css_support ) {
|
||||
$block_type->attributes['style'] = array(
|
||||
'type' => 'object',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Strips custom CSS (`style.css` in attributes) from all blocks in post content.
|
||||
*
|
||||
* Uses {@see WP_Block_Parser::next_token()} to scan block tokens and surgically
|
||||
* replace only the attribute JSON that changed — no parse_blocks() +
|
||||
* serialize_blocks() round-trip needed.
|
||||
*
|
||||
* @since 7.0.0
|
||||
* @access private
|
||||
*
|
||||
* @param string $content Post content to filter, expected to be escaped with slashes.
|
||||
* @return string Filtered post content with block custom CSS removed.
|
||||
*/
|
||||
function wp_strip_custom_css_from_blocks( $content ) {
|
||||
if ( ! has_blocks( $content ) ) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
$unslashed = stripslashes( $content );
|
||||
|
||||
$parser = new WP_Block_Parser();
|
||||
$parser->document = $unslashed;
|
||||
$parser->offset = 0;
|
||||
$end = strlen( $unslashed );
|
||||
$replacements = array();
|
||||
|
||||
while ( $parser->offset < $end ) {
|
||||
$next_token = $parser->next_token();
|
||||
|
||||
if ( 'no-more-tokens' === $next_token[0] ) {
|
||||
break;
|
||||
}
|
||||
|
||||
list( $token_type, , $attrs, $start_offset, $token_length ) = $next_token;
|
||||
|
||||
$parser->offset = $start_offset + $token_length;
|
||||
|
||||
if ( 'block-opener' !== $token_type && 'void-block' !== $token_type ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( ! isset( $attrs['style']['css'] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Remove css and clean up empty style.
|
||||
unset( $attrs['style']['css'] );
|
||||
if ( empty( $attrs['style'] ) ) {
|
||||
unset( $attrs['style'] );
|
||||
}
|
||||
|
||||
// Locate the JSON portion within the token.
|
||||
$token_string = substr( $unslashed, $start_offset, $token_length );
|
||||
$json_rel_start = strcspn( $token_string, '{' );
|
||||
$json_rel_end = strrpos( $token_string, '}' );
|
||||
|
||||
$json_start = $start_offset + $json_rel_start;
|
||||
$json_length = $json_rel_end - $json_rel_start + 1;
|
||||
|
||||
// Re-encode attributes. If attrs is now empty, remove JSON and trailing space.
|
||||
if ( empty( $attrs ) ) {
|
||||
// Remove the trailing space after JSON.
|
||||
$replacements[] = array( $json_start, $json_length + 1, '' );
|
||||
} else {
|
||||
$replacements[] = array( $json_start, $json_length, serialize_block_attributes( $attrs ) );
|
||||
}
|
||||
}
|
||||
|
||||
if ( empty( $replacements ) ) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
// Build the result by splicing replacements into the original string.
|
||||
$result = '';
|
||||
$was_at = 0;
|
||||
|
||||
foreach ( $replacements as $replacement ) {
|
||||
list( $offset, $length, $new_json ) = $replacement;
|
||||
$result .= substr( $unslashed, $was_at, $offset - $was_at ) . $new_json;
|
||||
$was_at = $offset + $length;
|
||||
}
|
||||
|
||||
if ( $was_at < $end ) {
|
||||
$result .= substr( $unslashed, $was_at );
|
||||
}
|
||||
|
||||
return addslashes( $result );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the filters to strip custom CSS from block content on save.
|
||||
* Priority of 8 to run before wp_filter_global_styles_post (priority 9) and wp_filter_post_kses (priority 10).
|
||||
*
|
||||
* @since 7.0.0
|
||||
* @access private
|
||||
*/
|
||||
function wp_custom_css_kses_init_filters() {
|
||||
add_filter( 'content_save_pre', 'wp_strip_custom_css_from_blocks', 8 );
|
||||
add_filter( 'content_filtered_save_pre', 'wp_strip_custom_css_from_blocks', 8 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the filters that strip custom CSS from block content on save.
|
||||
* Priority of 8 to run before wp_filter_global_styles_post (priority 9) and wp_filter_post_kses (priority 10).
|
||||
*
|
||||
* @since 7.0.0
|
||||
* @access private
|
||||
*/
|
||||
function wp_custom_css_remove_filters() {
|
||||
remove_filter( 'content_save_pre', 'wp_strip_custom_css_from_blocks', 8 );
|
||||
remove_filter( 'content_filtered_save_pre', 'wp_strip_custom_css_from_blocks', 8 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the custom CSS content filters if the user does not have the edit_css capability.
|
||||
*
|
||||
* @since 7.0.0
|
||||
* @access private
|
||||
*/
|
||||
function wp_custom_css_kses_init() {
|
||||
wp_custom_css_remove_filters();
|
||||
if ( ! current_user_can( 'edit_css' ) ) {
|
||||
wp_custom_css_kses_init_filters();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes custom CSS content filters when imported data should be filtered.
|
||||
*
|
||||
* Runs at priority 999 on {@see 'force_filtered_html_on_import'} to ensure it
|
||||
* fires after general KSES initialization, independently of user capabilities.
|
||||
* If the input of the filter is true it means we are in an import situation and should
|
||||
* enable the custom CSS filters, independently of the user capabilities.
|
||||
*
|
||||
* @since 7.0.0
|
||||
* @access private
|
||||
*
|
||||
* @param mixed $arg Input argument of the filter.
|
||||
* @return mixed Input argument of the filter.
|
||||
*/
|
||||
function wp_custom_css_force_filtered_html_on_import_filter( $arg ) {
|
||||
if ( $arg ) {
|
||||
wp_custom_css_kses_init_filters();
|
||||
}
|
||||
return $arg;
|
||||
}
|
||||
|
||||
// Run before wp_filter_global_styles_post (priority 9) and wp_filter_post_kses (priority 10).
|
||||
add_action( 'init', 'wp_custom_css_kses_init', 20 );
|
||||
add_action( 'set_current_user', 'wp_custom_css_kses_init' );
|
||||
add_filter( 'force_filtered_html_on_import', 'wp_custom_css_force_filtered_html_on_import_filter', 999 );
|
||||
|
||||
// Register the block support.
|
||||
WP_Block_Supports::get_instance()->register(
|
||||
'custom-css',
|
||||
array(
|
||||
'register_attribute' => 'wp_register_custom_css_support',
|
||||
)
|
||||
);
|
||||
@@ -0,0 +1,196 @@
|
||||
<?php
|
||||
/**
|
||||
* Dimensions block support flag.
|
||||
*
|
||||
* This does not include the `spacing` block support even though that visually
|
||||
* appears under the "Dimensions" panel in the editor. It remains in its
|
||||
* original `spacing.php` file for compatibility with core.
|
||||
*
|
||||
* @package WordPress
|
||||
* @since 5.9.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Registers the style block attribute for block types that support it.
|
||||
*
|
||||
* @since 5.9.0
|
||||
* @access private
|
||||
*
|
||||
* @param WP_Block_Type $block_type Block Type.
|
||||
*/
|
||||
function wp_register_dimensions_support( $block_type ) {
|
||||
// Setup attributes and styles within that if needed.
|
||||
if ( ! $block_type->attributes ) {
|
||||
$block_type->attributes = array();
|
||||
}
|
||||
|
||||
// Check for existing style attribute definition e.g. from block.json.
|
||||
if ( array_key_exists( 'style', $block_type->attributes ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$has_dimensions_support = block_has_support( $block_type, 'dimensions', false );
|
||||
|
||||
if ( $has_dimensions_support ) {
|
||||
$block_type->attributes['style'] = array(
|
||||
'type' => 'object',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds CSS classes for block dimensions to the incoming attributes array.
|
||||
* This will be applied to the block markup in the front-end.
|
||||
*
|
||||
* @since 5.9.0
|
||||
* @since 6.2.0 Added `minHeight` support.
|
||||
* @access private
|
||||
*
|
||||
* @param WP_Block_Type $block_type Block Type.
|
||||
* @param array $block_attributes Block attributes.
|
||||
* @return array Block dimensions CSS classes and inline styles.
|
||||
*/
|
||||
function wp_apply_dimensions_support( $block_type, $block_attributes ) {
|
||||
$attributes = array();
|
||||
|
||||
if ( wp_should_skip_block_supports_serialization( $block_type, 'dimensions' ) ) {
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
$block_styles = $block_attributes['style'] ?? null;
|
||||
|
||||
if ( ! $block_styles ) {
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
$dimensions_block_styles = array();
|
||||
$supported_features = array( 'minHeight', 'minWidth', 'height', 'width' );
|
||||
|
||||
foreach ( $supported_features as $feature ) {
|
||||
$has_support = block_has_support( $block_type, array( 'dimensions', $feature ), false );
|
||||
$skip_serialization = wp_should_skip_block_supports_serialization( $block_type, 'dimensions', $feature );
|
||||
|
||||
$dimensions_block_styles[ $feature ] = null;
|
||||
|
||||
if ( $has_support && ! $skip_serialization ) {
|
||||
$dimensions_block_styles[ $feature ] = $block_styles['dimensions'][ $feature ] ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
$styles = wp_style_engine_get_styles( array( 'dimensions' => $dimensions_block_styles ) );
|
||||
|
||||
if ( ! empty( $styles['css'] ) ) {
|
||||
$attributes['style'] = $styles['css'];
|
||||
}
|
||||
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether an aspectRatio block-support value is explicitly set.
|
||||
*
|
||||
* @since 7.1.0
|
||||
* @access private
|
||||
*
|
||||
* @param mixed $aspect_ratio Aspect-ratio value.
|
||||
* @return bool Whether the value is an explicit aspect ratio.
|
||||
*/
|
||||
function wp_is_explicit_aspect_ratio_value( $aspect_ratio ) {
|
||||
if ( ! is_string( $aspect_ratio ) && ! is_numeric( $aspect_ratio ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$aspect_ratio = strtolower( trim( (string) $aspect_ratio ) );
|
||||
|
||||
return '' !== $aspect_ratio && 'auto' !== $aspect_ratio;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders server-side dimensions styles to the block wrapper.
|
||||
* This block support uses the `render_block` hook to ensure that
|
||||
* it is also applied to non-server-rendered blocks.
|
||||
*
|
||||
* @since 6.5.0
|
||||
* @access private
|
||||
*
|
||||
* @param string $block_content Rendered block content.
|
||||
* @param array $block Block object.
|
||||
* @return string Filtered block content.
|
||||
*/
|
||||
function wp_render_dimensions_support( $block_content, $block ) {
|
||||
$block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] );
|
||||
$block_attributes = ( isset( $block['attrs'] ) && is_array( $block['attrs'] ) ) ? $block['attrs'] : array();
|
||||
$has_aspect_ratio_support = block_has_support( $block_type, array( 'dimensions', 'aspectRatio' ), false );
|
||||
|
||||
if (
|
||||
! $has_aspect_ratio_support ||
|
||||
wp_should_skip_block_supports_serialization( $block_type, 'dimensions', 'aspectRatio' )
|
||||
) {
|
||||
return $block_content;
|
||||
}
|
||||
|
||||
$dimensions_block_styles = array();
|
||||
$dimensions_block_styles['aspectRatio'] = $block_attributes['style']['dimensions']['aspectRatio'] ?? null;
|
||||
|
||||
// To ensure the aspect ratio does not get overridden by `minHeight` or `height` unset any existing rule.
|
||||
if (
|
||||
wp_is_explicit_aspect_ratio_value( $dimensions_block_styles['aspectRatio'] )
|
||||
) {
|
||||
$dimensions_block_styles['minHeight'] = 'unset';
|
||||
$dimensions_block_styles['height'] = 'unset';
|
||||
} elseif (
|
||||
isset( $block_attributes['style']['dimensions']['minHeight'] ) ||
|
||||
isset( $block_attributes['minHeight'] )
|
||||
) {
|
||||
$dimensions_block_styles['aspectRatio'] = 'unset';
|
||||
}
|
||||
|
||||
$styles = wp_style_engine_get_styles( array( 'dimensions' => $dimensions_block_styles ) );
|
||||
|
||||
if ( ! empty( $styles['css'] ) ) {
|
||||
// Inject dimensions styles to the first element, presuming it's the wrapper, if it exists.
|
||||
$tags = new WP_HTML_Tag_Processor( $block_content );
|
||||
|
||||
if ( $tags->next_tag() ) {
|
||||
$existing_style = $tags->get_attribute( 'style' );
|
||||
$updated_style = '';
|
||||
|
||||
if ( ! empty( $existing_style ) ) {
|
||||
$updated_style = $existing_style;
|
||||
if ( ! str_ends_with( $existing_style, ';' ) ) {
|
||||
$updated_style .= ';';
|
||||
}
|
||||
}
|
||||
|
||||
$updated_style .= $styles['css'];
|
||||
$tags->set_attribute( 'style', $updated_style );
|
||||
|
||||
if ( ! empty( $styles['classnames'] ) ) {
|
||||
foreach ( explode( ' ', $styles['classnames'] ) as $class_name ) {
|
||||
if (
|
||||
str_contains( $class_name, 'aspect-ratio' ) &&
|
||||
! wp_is_explicit_aspect_ratio_value( $block_attributes['style']['dimensions']['aspectRatio'] ?? null )
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
$tags->add_class( $class_name );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $tags->get_updated_html();
|
||||
}
|
||||
|
||||
return $block_content;
|
||||
}
|
||||
|
||||
add_filter( 'render_block', 'wp_render_dimensions_support', 10, 2 );
|
||||
|
||||
// Register the block support.
|
||||
WP_Block_Supports::get_instance()->register(
|
||||
'dimensions',
|
||||
array(
|
||||
'register_attribute' => 'wp_register_dimensions_support',
|
||||
'apply' => 'wp_apply_dimensions_support',
|
||||
)
|
||||
);
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
/**
|
||||
* Duotone block support flag.
|
||||
*
|
||||
* Parts of this source were derived and modified from TinyColor,
|
||||
* released under the MIT license.
|
||||
*
|
||||
* https://github.com/bgrins/TinyColor
|
||||
*
|
||||
* Copyright (c), Brian Grinstead, http://briangrinstead.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* @package WordPress
|
||||
* @since 5.8.0
|
||||
*/
|
||||
|
||||
// Register the block support.
|
||||
WP_Block_Supports::get_instance()->register(
|
||||
'duotone',
|
||||
array(
|
||||
'register_attribute' => array( 'WP_Duotone', 'register_duotone_support' ),
|
||||
)
|
||||
);
|
||||
|
||||
// Add classnames to blocks using duotone support.
|
||||
add_filter( 'render_block', array( 'WP_Duotone', 'render_duotone_support' ), 10, 3 );
|
||||
add_filter( 'render_block_core/image', array( 'WP_Duotone', 'restore_image_outer_container' ), 10, 1 );
|
||||
|
||||
// Enqueue styles.
|
||||
// Block styles (core-block-supports-inline-css) before the style engine (wp_enqueue_stored_styles).
|
||||
// Global styles (global-styles-inline-css) after the other global styles (wp_enqueue_global_styles).
|
||||
add_action( 'wp_enqueue_scripts', array( 'WP_Duotone', 'output_block_styles' ), 9 );
|
||||
add_action( 'wp_enqueue_scripts', array( 'WP_Duotone', 'output_global_styles' ), 11 );
|
||||
|
||||
// Add SVG filters to the footer. Also, for classic themes, output block styles (core-block-supports-inline-css).
|
||||
add_action( 'wp_footer', array( 'WP_Duotone', 'output_footer_assets' ), 10 );
|
||||
|
||||
// Add styles and SVGs for use in the editor via the EditorStyles component.
|
||||
add_filter( 'block_editor_settings_all', array( 'WP_Duotone', 'add_editor_settings' ), 10 );
|
||||
|
||||
// Migrate the old experimental duotone support flag.
|
||||
add_filter( 'block_type_metadata_settings', array( 'WP_Duotone', 'migrate_experimental_duotone_support_flag' ), 10, 2 );
|
||||
@@ -0,0 +1,308 @@
|
||||
<?php
|
||||
/**
|
||||
* Elements styles block support.
|
||||
*
|
||||
* @package WordPress
|
||||
* @since 5.8.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Gets the elements class names.
|
||||
*
|
||||
* @since 6.0.0
|
||||
* @access private
|
||||
*
|
||||
* @return string The unique class name.
|
||||
*/
|
||||
function wp_get_elements_class_name(): string {
|
||||
return wp_unique_prefixed_id( 'wp-elements-' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether an elements class name should be added to the block.
|
||||
*
|
||||
* @since 6.6.0
|
||||
* @access private
|
||||
*
|
||||
* @param array $block Block object.
|
||||
* @param array $options Per element type options e.g. whether to skip serialization.
|
||||
* @return bool Whether the block needs an elements class name.
|
||||
*/
|
||||
function wp_should_add_elements_class_name( $block, $options ) {
|
||||
if ( ! isset( $block['attrs']['style']['elements'] ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$element_color_properties = array(
|
||||
'button' => array(
|
||||
'skip' => $options['button']['skip'] ?? false,
|
||||
'paths' => array(
|
||||
array( 'button', 'color', 'text' ),
|
||||
array( 'button', 'color', 'background' ),
|
||||
array( 'button', 'color', 'gradient' ),
|
||||
),
|
||||
),
|
||||
'link' => array(
|
||||
'skip' => $options['link']['skip'] ?? false,
|
||||
'paths' => array(
|
||||
array( 'link', 'color', 'text' ),
|
||||
array( 'link', ':hover', 'color', 'text' ),
|
||||
),
|
||||
),
|
||||
'heading' => array(
|
||||
'skip' => $options['heading']['skip'] ?? false,
|
||||
'paths' => array(
|
||||
array( 'heading', 'color', 'text' ),
|
||||
array( 'heading', 'color', 'background' ),
|
||||
array( 'heading', 'color', 'gradient' ),
|
||||
array( 'h1', 'color', 'text' ),
|
||||
array( 'h1', 'color', 'background' ),
|
||||
array( 'h1', 'color', 'gradient' ),
|
||||
array( 'h2', 'color', 'text' ),
|
||||
array( 'h2', 'color', 'background' ),
|
||||
array( 'h2', 'color', 'gradient' ),
|
||||
array( 'h3', 'color', 'text' ),
|
||||
array( 'h3', 'color', 'background' ),
|
||||
array( 'h3', 'color', 'gradient' ),
|
||||
array( 'h4', 'color', 'text' ),
|
||||
array( 'h4', 'color', 'background' ),
|
||||
array( 'h4', 'color', 'gradient' ),
|
||||
array( 'h5', 'color', 'text' ),
|
||||
array( 'h5', 'color', 'background' ),
|
||||
array( 'h5', 'color', 'gradient' ),
|
||||
array( 'h6', 'color', 'text' ),
|
||||
array( 'h6', 'color', 'background' ),
|
||||
array( 'h6', 'color', 'gradient' ),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
$elements_style_attributes = $block['attrs']['style']['elements'];
|
||||
|
||||
foreach ( $element_color_properties as $element_config ) {
|
||||
if ( $element_config['skip'] ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ( $element_config['paths'] as $path ) {
|
||||
if ( null !== _wp_array_get( $elements_style_attributes, $path, null ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the elements stylesheet and adds elements class name to block as required.
|
||||
*
|
||||
* In the case of nested blocks we want the parent element styles to be rendered before their descendants.
|
||||
* This solves the issue of an element (e.g.: link color) being styled in both the parent and a descendant:
|
||||
* we want the descendant style to take priority, and this is done by loading it after, in DOM order.
|
||||
*
|
||||
* @since 6.0.0
|
||||
* @since 6.1.0 Implemented the style engine to generate CSS and classnames.
|
||||
* @since 6.6.0 Element block support class and styles are generated via the `render_block_data` filter instead of `pre_render_block`.
|
||||
* @access private
|
||||
*
|
||||
* @param array $parsed_block The parsed block.
|
||||
* @return array The same parsed block with elements classname added if appropriate.
|
||||
*
|
||||
* @phpstan-param array{
|
||||
* blockName: string,
|
||||
* attrs: array{
|
||||
* className?: string,
|
||||
* style?: array{
|
||||
* elements?: array<string, array{
|
||||
* ":hover"?: array<string, string>,
|
||||
* ...
|
||||
* }>,
|
||||
* },
|
||||
* ...
|
||||
* },
|
||||
* ...
|
||||
* } $parsed_block
|
||||
* @phpstan-return array{
|
||||
* blockName: string,
|
||||
* attrs: array{
|
||||
* className?: string,
|
||||
* ...
|
||||
* },
|
||||
* ...
|
||||
* }
|
||||
*/
|
||||
function wp_render_elements_support_styles( $parsed_block ) {
|
||||
/*
|
||||
* The generation of element styles and classname were moved to the
|
||||
* `render_block_data` filter in 6.6.0 to avoid filtered attributes
|
||||
* breaking the application of the elements CSS class.
|
||||
*
|
||||
* @link https://github.com/WordPress/gutenberg/pull/59535
|
||||
*
|
||||
* The change in filter means, the argument types for this function
|
||||
* have changed and require deprecating.
|
||||
*/
|
||||
if ( is_string( $parsed_block ) ) {
|
||||
_deprecated_argument(
|
||||
__FUNCTION__,
|
||||
'6.6.0',
|
||||
__( 'Use as a `pre_render_block` filter is deprecated. Use with `render_block_data` instead.' )
|
||||
);
|
||||
}
|
||||
|
||||
$block_type = WP_Block_Type_Registry::get_instance()->get_registered( $parsed_block['blockName'] );
|
||||
if ( ! $block_type ) {
|
||||
return $parsed_block;
|
||||
}
|
||||
|
||||
$element_block_styles = $parsed_block['attrs']['style']['elements'] ?? null;
|
||||
if ( ! $element_block_styles ) {
|
||||
return $parsed_block;
|
||||
}
|
||||
|
||||
$skip_link_color_serialization = wp_should_skip_block_supports_serialization( $block_type, 'color', 'link' );
|
||||
$skip_heading_color_serialization = wp_should_skip_block_supports_serialization( $block_type, 'color', 'heading' );
|
||||
$skip_button_color_serialization = wp_should_skip_block_supports_serialization( $block_type, 'color', 'button' );
|
||||
$skips_all_element_color_serialization = $skip_link_color_serialization &&
|
||||
$skip_heading_color_serialization &&
|
||||
$skip_button_color_serialization;
|
||||
|
||||
if ( $skips_all_element_color_serialization ) {
|
||||
return $parsed_block;
|
||||
}
|
||||
|
||||
$options = array(
|
||||
'button' => array( 'skip' => $skip_button_color_serialization ),
|
||||
'link' => array( 'skip' => $skip_link_color_serialization ),
|
||||
'heading' => array( 'skip' => $skip_heading_color_serialization ),
|
||||
);
|
||||
|
||||
if ( ! wp_should_add_elements_class_name( $parsed_block, $options ) ) {
|
||||
return $parsed_block;
|
||||
}
|
||||
|
||||
$class_name = wp_get_elements_class_name();
|
||||
$updated_class_name = isset( $parsed_block['attrs']['className'] ) ? $parsed_block['attrs']['className'] . " $class_name" : $class_name;
|
||||
|
||||
_wp_array_set( $parsed_block, array( 'attrs', 'className' ), $updated_class_name );
|
||||
|
||||
// Generate element styles based on selector and store in style engine for enqueuing.
|
||||
$element_types = array(
|
||||
'button' => array(
|
||||
'selector' => ".$class_name .wp-element-button, .$class_name .wp-block-button__link",
|
||||
'skip' => $skip_button_color_serialization,
|
||||
),
|
||||
'link' => array(
|
||||
'selector' => ".$class_name a:where(:not(.wp-element-button))",
|
||||
'hover_selector' => ".$class_name a:where(:not(.wp-element-button)):hover",
|
||||
'skip' => $skip_link_color_serialization,
|
||||
),
|
||||
'heading' => array(
|
||||
'selector' => ".$class_name h1, .$class_name h2, .$class_name h3, .$class_name h4, .$class_name h5, .$class_name h6",
|
||||
'skip' => $skip_heading_color_serialization,
|
||||
'elements' => array( 'h1', 'h2', 'h3', 'h4', 'h5', 'h6' ),
|
||||
),
|
||||
);
|
||||
|
||||
foreach ( $element_types as $element_type => $element_config ) {
|
||||
if ( $element_config['skip'] ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$element_style_object = $element_block_styles[ $element_type ] ?? null;
|
||||
|
||||
// Process primary element type styles.
|
||||
if ( $element_style_object ) {
|
||||
wp_style_engine_get_styles(
|
||||
$element_style_object,
|
||||
array(
|
||||
'selector' => $element_config['selector'],
|
||||
'context' => 'block-supports',
|
||||
)
|
||||
);
|
||||
|
||||
if ( isset( $element_style_object[':hover'], $element_config['hover_selector'] ) ) {
|
||||
wp_style_engine_get_styles(
|
||||
$element_style_object[':hover'],
|
||||
array(
|
||||
'selector' => $element_config['hover_selector'],
|
||||
'context' => 'block-supports',
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Process related elements e.g. h1-h6 for headings.
|
||||
if ( isset( $element_config['elements'] ) ) {
|
||||
foreach ( $element_config['elements'] as $element ) {
|
||||
$element_style_object = $element_block_styles[ $element ] ?? null;
|
||||
|
||||
if ( $element_style_object ) {
|
||||
wp_style_engine_get_styles(
|
||||
$element_style_object,
|
||||
array(
|
||||
'selector' => ".$class_name $element",
|
||||
'context' => 'block-supports',
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $parsed_block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the elements block support class name generated, and added to
|
||||
* block attributes, in the `render_block_data` filter gets applied to the
|
||||
* block's markup.
|
||||
*
|
||||
* @see wp_render_elements_support_styles
|
||||
* @since 6.6.0
|
||||
*
|
||||
* @param string $block_content Rendered block content.
|
||||
* @param array $block Block object.
|
||||
* @return string Filtered block content.
|
||||
*
|
||||
* @phpstan-param array{
|
||||
* attrs: array{
|
||||
* className?: string,
|
||||
* ...
|
||||
* },
|
||||
* ...
|
||||
* } $block
|
||||
*/
|
||||
function wp_render_elements_class_name( $block_content, $block ) {
|
||||
$class_name_attr = $block['attrs']['className'] ?? null;
|
||||
$class_name_prefix = 'wp-elements-';
|
||||
if ( ! is_string( $class_name_attr ) || ! str_contains( $class_name_attr, $class_name_prefix ) ) {
|
||||
return $block_content;
|
||||
}
|
||||
|
||||
// Parse out the 'wp-elements-*' class name.
|
||||
$matched_class_name = null;
|
||||
$token_delimiter = " \t\f\r\n";
|
||||
$class_token = strtok( $class_name_attr, $token_delimiter );
|
||||
while ( false !== $class_token ) {
|
||||
if ( str_starts_with( $class_token, $class_name_prefix ) ) {
|
||||
$matched_class_name = $class_token;
|
||||
break;
|
||||
}
|
||||
$class_token = strtok( $token_delimiter );
|
||||
}
|
||||
if ( null === $matched_class_name ) {
|
||||
return $block_content;
|
||||
}
|
||||
|
||||
$tags = new WP_HTML_Tag_Processor( $block_content );
|
||||
if ( $tags->next_tag() ) {
|
||||
$tags->add_class( $matched_class_name );
|
||||
}
|
||||
|
||||
return $tags->get_updated_html();
|
||||
}
|
||||
|
||||
add_filter( 'render_block', 'wp_render_elements_class_name', 10, 2 );
|
||||
add_filter( 'render_block_data', 'wp_render_elements_support_styles', 10, 1 );
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
/**
|
||||
* Generated classname block support flag.
|
||||
*
|
||||
* @package WordPress
|
||||
* @since 5.6.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Gets the generated classname from a given block name.
|
||||
*
|
||||
* @since 5.6.0
|
||||
*
|
||||
* @access private
|
||||
*
|
||||
* @param string $block_name Block Name.
|
||||
* @return string Generated classname.
|
||||
*/
|
||||
function wp_get_block_default_classname( $block_name ) {
|
||||
// Generated HTML classes for blocks follow the `wp-block-{name}` nomenclature.
|
||||
// Blocks provided by WordPress drop the prefixes 'core/' or 'core-' (historically used in 'core-embed/').
|
||||
$classname = 'wp-block-' . preg_replace(
|
||||
'/^core-/',
|
||||
'',
|
||||
str_replace( '/', '-', $block_name )
|
||||
);
|
||||
|
||||
/**
|
||||
* Filters the default block className for server rendered blocks.
|
||||
*
|
||||
* @since 5.6.0
|
||||
*
|
||||
* @param string $class_name The current applied classname.
|
||||
* @param string $block_name The block name.
|
||||
*/
|
||||
$classname = apply_filters( 'block_default_classname', $classname, $block_name );
|
||||
|
||||
return $classname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the generated classnames to the output.
|
||||
*
|
||||
* @since 5.6.0
|
||||
*
|
||||
* @access private
|
||||
*
|
||||
* @param WP_Block_Type $block_type Block Type.
|
||||
* @return array Block CSS classes and inline styles.
|
||||
*/
|
||||
function wp_apply_generated_classname_support( $block_type ) {
|
||||
$attributes = array();
|
||||
$has_generated_classname_support = block_has_support( $block_type, 'className', true );
|
||||
if ( $has_generated_classname_support ) {
|
||||
$block_classname = wp_get_block_default_classname( $block_type->name );
|
||||
|
||||
if ( $block_classname ) {
|
||||
$attributes['class'] = $block_classname;
|
||||
}
|
||||
}
|
||||
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
// Register the block support.
|
||||
WP_Block_Supports::get_instance()->register(
|
||||
'generated-classname',
|
||||
array(
|
||||
'apply' => 'wp_apply_generated_classname_support',
|
||||
)
|
||||
);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,151 @@
|
||||
<?php
|
||||
/**
|
||||
* Position block support flag.
|
||||
*
|
||||
* @package WordPress
|
||||
* @since 6.2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Registers the style block attribute for block types that support it.
|
||||
*
|
||||
* @since 6.2.0
|
||||
* @access private
|
||||
*
|
||||
* @param WP_Block_Type $block_type Block Type.
|
||||
*/
|
||||
function wp_register_position_support( $block_type ) {
|
||||
$has_position_support = block_has_support( $block_type, 'position', false );
|
||||
|
||||
// Set up attributes and styles within that if needed.
|
||||
if ( ! $block_type->attributes ) {
|
||||
$block_type->attributes = array();
|
||||
}
|
||||
|
||||
if ( $has_position_support && ! array_key_exists( 'style', $block_type->attributes ) ) {
|
||||
$block_type->attributes['style'] = array(
|
||||
'type' => 'object',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders position styles to the block wrapper.
|
||||
*
|
||||
* @since 6.2.0
|
||||
* @access private
|
||||
*
|
||||
* @param string $block_content Rendered block content.
|
||||
* @param array $block Block object.
|
||||
* @return string Filtered block content.
|
||||
*/
|
||||
function wp_render_position_support( $block_content, $block ) {
|
||||
$block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] );
|
||||
$has_position_support = block_has_support( $block_type, 'position', false );
|
||||
|
||||
if (
|
||||
! $has_position_support ||
|
||||
empty( $block['attrs']['style']['position'] )
|
||||
) {
|
||||
return $block_content;
|
||||
}
|
||||
|
||||
$global_settings = wp_get_global_settings();
|
||||
$theme_has_sticky_support = $global_settings['position']['sticky'] ?? false;
|
||||
$theme_has_fixed_support = $global_settings['position']['fixed'] ?? false;
|
||||
|
||||
// Only allow output for position types that the theme supports.
|
||||
$allowed_position_types = array();
|
||||
if ( true === $theme_has_sticky_support ) {
|
||||
$allowed_position_types[] = 'sticky';
|
||||
}
|
||||
if ( true === $theme_has_fixed_support ) {
|
||||
$allowed_position_types[] = 'fixed';
|
||||
}
|
||||
|
||||
$style_attribute = $block['attrs']['style'] ?? null;
|
||||
$class_name = wp_unique_id( 'wp-container-' );
|
||||
$selector = ".$class_name";
|
||||
$position_styles = array();
|
||||
$position_type = $style_attribute['position']['type'] ?? '';
|
||||
$wrapper_classes = array();
|
||||
|
||||
if (
|
||||
in_array( $position_type, $allowed_position_types, true )
|
||||
) {
|
||||
$wrapper_classes[] = $class_name;
|
||||
$wrapper_classes[] = 'is-position-' . $position_type;
|
||||
$sides = array( 'top', 'right', 'bottom', 'left' );
|
||||
|
||||
foreach ( $sides as $side ) {
|
||||
$side_value = $style_attribute['position'][ $side ] ?? null;
|
||||
if ( null !== $side_value ) {
|
||||
/*
|
||||
* For fixed or sticky top positions,
|
||||
* ensure the value includes an offset for the logged in admin bar.
|
||||
*/
|
||||
if (
|
||||
'top' === $side &&
|
||||
( 'fixed' === $position_type || 'sticky' === $position_type )
|
||||
) {
|
||||
// Ensure 0 values can be used in `calc()` calculations.
|
||||
if ( '0' === $side_value || 0 === $side_value ) {
|
||||
$side_value = '0px';
|
||||
}
|
||||
|
||||
// Ensure current side value also factors in the height of the logged in admin bar.
|
||||
$side_value = "calc($side_value + var(--wp-admin--admin-bar--position-offset, 0px))";
|
||||
}
|
||||
|
||||
$position_styles[] =
|
||||
array(
|
||||
'selector' => $selector,
|
||||
'declarations' => array(
|
||||
$side => $side_value,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
$position_styles[] =
|
||||
array(
|
||||
'selector' => $selector,
|
||||
'declarations' => array(
|
||||
'position' => $position_type,
|
||||
'z-index' => '10',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
if ( ! empty( $position_styles ) ) {
|
||||
/*
|
||||
* Add to the style engine store to enqueue and render position styles.
|
||||
*/
|
||||
wp_style_engine_get_stylesheet_from_css_rules(
|
||||
$position_styles,
|
||||
array(
|
||||
'context' => 'block-supports',
|
||||
'prettify' => false,
|
||||
)
|
||||
);
|
||||
|
||||
// Inject class name to block container markup.
|
||||
$content = new WP_HTML_Tag_Processor( $block_content );
|
||||
$content->next_tag();
|
||||
foreach ( $wrapper_classes as $class ) {
|
||||
$content->add_class( $class );
|
||||
}
|
||||
return (string) $content;
|
||||
}
|
||||
|
||||
return $block_content;
|
||||
}
|
||||
|
||||
// Register the block support.
|
||||
WP_Block_Supports::get_instance()->register(
|
||||
'position',
|
||||
array(
|
||||
'register_attribute' => 'wp_register_position_support',
|
||||
)
|
||||
);
|
||||
add_filter( 'render_block', 'wp_render_position_support', 10, 2 );
|
||||
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
/**
|
||||
* Block level presets support.
|
||||
*
|
||||
* @package WordPress
|
||||
* @since 6.2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get the class name used on block level presets.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @since 6.2.0
|
||||
* @access private
|
||||
*
|
||||
* @param array $block Block object.
|
||||
* @return string The unique class name.
|
||||
*/
|
||||
function _wp_get_presets_class_name( $block ) {
|
||||
return 'wp-settings-' . md5( serialize( $block ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the block content with block level presets class name.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @since 6.2.0
|
||||
* @access private
|
||||
*
|
||||
* @param string $block_content Rendered block content.
|
||||
* @param array $block Block object.
|
||||
* @return string Filtered block content.
|
||||
*/
|
||||
function _wp_add_block_level_presets_class( $block_content, $block ) {
|
||||
if ( ! $block_content ) {
|
||||
return $block_content;
|
||||
}
|
||||
|
||||
// return early if the block doesn't have support for settings.
|
||||
$block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] );
|
||||
if ( ! block_has_support( $block_type, '__experimentalSettings', false ) ) {
|
||||
return $block_content;
|
||||
}
|
||||
|
||||
// return early if no settings are found on the block attributes.
|
||||
$block_settings = $block['attrs']['settings'] ?? null;
|
||||
if ( empty( $block_settings ) ) {
|
||||
return $block_content;
|
||||
}
|
||||
|
||||
// Like the layout hook this assumes the hook only applies to blocks with a single wrapper.
|
||||
// Add the class name to the first element, presuming it's the wrapper, if it exists.
|
||||
$tags = new WP_HTML_Tag_Processor( $block_content );
|
||||
if ( $tags->next_tag() ) {
|
||||
$tags->add_class( _wp_get_presets_class_name( $block ) );
|
||||
}
|
||||
|
||||
return $tags->get_updated_html();
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the block level presets stylesheet.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @since 6.2.0
|
||||
* @since 6.3.0 Updated preset styles to use Selectors API.
|
||||
* @access private
|
||||
*
|
||||
* @param string|null $pre_render The pre-rendered content. Default null.
|
||||
* @param array $block The block being rendered.
|
||||
*
|
||||
* @return null
|
||||
*/
|
||||
function _wp_add_block_level_preset_styles( $pre_render, $block ) {
|
||||
// Return early if the block has not support for descendent block styles.
|
||||
$block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block['blockName'] );
|
||||
if ( ! block_has_support( $block_type, '__experimentalSettings', false ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// return early if no settings are found on the block attributes.
|
||||
$block_settings = $block['attrs']['settings'] ?? null;
|
||||
if ( empty( $block_settings ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$class_name = '.' . _wp_get_presets_class_name( $block );
|
||||
|
||||
// the root selector for preset variables needs to target every possible block selector
|
||||
// in order for the general setting to override any bock specific setting of a parent block or
|
||||
// the site root.
|
||||
$variables_root_selector = '*,[class*="wp-block"]';
|
||||
$registry = WP_Block_Type_Registry::get_instance();
|
||||
$blocks = $registry->get_all_registered();
|
||||
foreach ( $blocks as $block_type ) {
|
||||
/*
|
||||
* We only want to append selectors for blocks using custom selectors
|
||||
* i.e. not `wp-block-<name>`.
|
||||
*/
|
||||
$has_custom_selector =
|
||||
( isset( $block_type->supports['__experimentalSelector'] ) && is_string( $block_type->supports['__experimentalSelector'] ) ) ||
|
||||
( isset( $block_type->selectors['root'] ) && is_string( $block_type->selectors['root'] ) );
|
||||
|
||||
if ( $has_custom_selector ) {
|
||||
$variables_root_selector .= ',' . wp_get_block_css_selector( $block_type );
|
||||
}
|
||||
}
|
||||
$variables_root_selector = WP_Theme_JSON::scope_selector( $class_name, $variables_root_selector );
|
||||
|
||||
// Remove any potentially unsafe styles.
|
||||
$theme_json_shape = WP_Theme_JSON::remove_insecure_properties(
|
||||
array(
|
||||
'version' => WP_Theme_JSON::LATEST_SCHEMA,
|
||||
'settings' => $block_settings,
|
||||
)
|
||||
);
|
||||
$theme_json_object = new WP_Theme_JSON( $theme_json_shape );
|
||||
|
||||
$styles = '';
|
||||
|
||||
// include preset css variables declaration on the stylesheet.
|
||||
$styles .= $theme_json_object->get_stylesheet(
|
||||
array( 'variables' ),
|
||||
null,
|
||||
array(
|
||||
'root_selector' => $variables_root_selector,
|
||||
'scope' => $class_name,
|
||||
)
|
||||
);
|
||||
|
||||
// include preset css classes on the stylesheet.
|
||||
$styles .= $theme_json_object->get_stylesheet(
|
||||
array( 'presets' ),
|
||||
null,
|
||||
array(
|
||||
'root_selector' => $class_name . ',' . $class_name . ' *',
|
||||
'scope' => $class_name,
|
||||
)
|
||||
);
|
||||
|
||||
if ( ! empty( $styles ) ) {
|
||||
wp_enqueue_block_support_styles( $styles );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
add_filter( 'render_block', '_wp_add_block_level_presets_class', 10, 2 );
|
||||
add_filter( 'pre_render_block', '_wp_add_block_level_preset_styles', 10, 2 );
|
||||
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
/**
|
||||
* Shadow block support flag.
|
||||
*
|
||||
* @package WordPress
|
||||
* @since 6.3.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Registers the style and shadow block attributes for block types that support it.
|
||||
*
|
||||
* @since 6.3.0
|
||||
* @access private
|
||||
*
|
||||
* @param WP_Block_Type $block_type Block Type.
|
||||
*/
|
||||
function wp_register_shadow_support( $block_type ) {
|
||||
$has_shadow_support = block_has_support( $block_type, 'shadow', false );
|
||||
|
||||
if ( ! $has_shadow_support ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! $block_type->attributes ) {
|
||||
$block_type->attributes = array();
|
||||
}
|
||||
|
||||
if ( array_key_exists( 'style', $block_type->attributes ) ) {
|
||||
$block_type->attributes['style'] = array(
|
||||
'type' => 'object',
|
||||
);
|
||||
}
|
||||
|
||||
if ( array_key_exists( 'shadow', $block_type->attributes ) ) {
|
||||
$block_type->attributes['shadow'] = array(
|
||||
'type' => 'string',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add CSS classes and inline styles for shadow features to the incoming attributes array.
|
||||
* This will be applied to the block markup in the front-end.
|
||||
*
|
||||
* @since 6.3.0
|
||||
* @since 6.6.0 Return early if __experimentalSkipSerialization is true.
|
||||
* @access private
|
||||
*
|
||||
* @param WP_Block_Type $block_type Block type.
|
||||
* @param array $block_attributes Block attributes.
|
||||
* @return array Shadow CSS classes and inline styles.
|
||||
*/
|
||||
function wp_apply_shadow_support( $block_type, $block_attributes ) {
|
||||
$has_shadow_support = block_has_support( $block_type, 'shadow', false );
|
||||
|
||||
if (
|
||||
! $has_shadow_support ||
|
||||
wp_should_skip_block_supports_serialization( $block_type, 'shadow' )
|
||||
) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$shadow_block_styles = array();
|
||||
|
||||
$custom_shadow = $block_attributes['style']['shadow'] ?? null;
|
||||
$shadow_block_styles['shadow'] = $custom_shadow;
|
||||
|
||||
$attributes = array();
|
||||
$styles = wp_style_engine_get_styles( $shadow_block_styles );
|
||||
|
||||
if ( ! empty( $styles['css'] ) ) {
|
||||
$attributes['style'] = $styles['css'];
|
||||
}
|
||||
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
// Register the block support.
|
||||
WP_Block_Supports::get_instance()->register(
|
||||
'shadow',
|
||||
array(
|
||||
'register_attribute' => 'wp_register_shadow_support',
|
||||
'apply' => 'wp_apply_shadow_support',
|
||||
)
|
||||
);
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
/**
|
||||
* Spacing block support flag.
|
||||
*
|
||||
* For backwards compatibility, this remains separate to the dimensions.php
|
||||
* block support despite both belonging under a single panel in the editor.
|
||||
*
|
||||
* @package WordPress
|
||||
* @since 5.8.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Registers the style block attribute for block types that support it.
|
||||
*
|
||||
* @since 5.8.0
|
||||
* @access private
|
||||
*
|
||||
* @param WP_Block_Type $block_type Block Type.
|
||||
*/
|
||||
function wp_register_spacing_support( $block_type ) {
|
||||
$has_spacing_support = block_has_support( $block_type, 'spacing', false );
|
||||
|
||||
// Setup attributes and styles within that if needed.
|
||||
if ( ! $block_type->attributes ) {
|
||||
$block_type->attributes = array();
|
||||
}
|
||||
|
||||
if ( $has_spacing_support && ! array_key_exists( 'style', $block_type->attributes ) ) {
|
||||
$block_type->attributes['style'] = array(
|
||||
'type' => 'object',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds CSS classes for block spacing to the incoming attributes array.
|
||||
* This will be applied to the block markup in the front-end.
|
||||
*
|
||||
* @since 5.8.0
|
||||
* @since 6.1.0 Implemented the style engine to generate CSS and classnames.
|
||||
* @access private
|
||||
*
|
||||
* @param WP_Block_Type $block_type Block Type.
|
||||
* @param array $block_attributes Block attributes.
|
||||
* @return array Block spacing CSS classes and inline styles.
|
||||
*/
|
||||
function wp_apply_spacing_support( $block_type, $block_attributes ) {
|
||||
if ( wp_should_skip_block_supports_serialization( $block_type, 'spacing' ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$attributes = array();
|
||||
$has_padding_support = block_has_support( $block_type, array( 'spacing', 'padding' ), false );
|
||||
$has_margin_support = block_has_support( $block_type, array( 'spacing', 'margin' ), false );
|
||||
$block_styles = $block_attributes['style'] ?? null;
|
||||
|
||||
if ( ! $block_styles ) {
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
$skip_padding = wp_should_skip_block_supports_serialization( $block_type, 'spacing', 'padding' );
|
||||
$skip_margin = wp_should_skip_block_supports_serialization( $block_type, 'spacing', 'margin' );
|
||||
$spacing_block_styles = array(
|
||||
'padding' => null,
|
||||
'margin' => null,
|
||||
);
|
||||
if ( $has_padding_support && ! $skip_padding ) {
|
||||
$spacing_block_styles['padding'] = $block_styles['spacing']['padding'] ?? null;
|
||||
}
|
||||
if ( $has_margin_support && ! $skip_margin ) {
|
||||
$spacing_block_styles['margin'] = $block_styles['spacing']['margin'] ?? null;
|
||||
}
|
||||
$styles = wp_style_engine_get_styles( array( 'spacing' => $spacing_block_styles ) );
|
||||
|
||||
if ( ! empty( $styles['css'] ) ) {
|
||||
$attributes['style'] = $styles['css'];
|
||||
}
|
||||
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
// Register the block support.
|
||||
WP_Block_Supports::get_instance()->register(
|
||||
'spacing',
|
||||
array(
|
||||
'register_attribute' => 'wp_register_spacing_support',
|
||||
'apply' => 'wp_apply_spacing_support',
|
||||
)
|
||||
);
|
||||
@@ -0,0 +1,740 @@
|
||||
<?php
|
||||
/**
|
||||
* Block state support for frontend CSS generation.
|
||||
*
|
||||
* Generates scoped CSS for per-instance state styles declared in block attributes,
|
||||
* including pseudo-states (e.g., `style[':hover']`) and responsive states
|
||||
* (e.g., `style['@mobile']` and `style['@mobile'][':hover']`).
|
||||
*
|
||||
* @package WordPress
|
||||
* @since 7.1.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Converts internal preset references to CSS custom property references.
|
||||
*
|
||||
* State styles are emitted as CSS rules and cannot rely on preset classnames.
|
||||
* Converting `var:preset|color|contrast` to
|
||||
* `var(--wp--preset--color--contrast)` ensures preset values are emitted as
|
||||
* declarations by the style engine.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @param mixed $value Style value to normalize.
|
||||
* @return mixed Normalized style value.
|
||||
*/
|
||||
function wp_normalize_state_preset_vars( $value ) {
|
||||
if ( is_array( $value ) ) {
|
||||
foreach ( $value as $key => $nested_value ) {
|
||||
$value[ $key ] = wp_normalize_state_preset_vars( $nested_value );
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
if ( ! is_string( $value ) || ! str_starts_with( $value, 'var:preset|' ) ) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
$unwrapped_name = str_replace( '|', '--', substr( $value, strlen( 'var:' ) ) );
|
||||
return "var(--wp--$unwrapped_name)";
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes a state style object before generating CSS declarations.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @param array $style State style object.
|
||||
* @return array Normalized state style object.
|
||||
*/
|
||||
function wp_normalize_state_style_for_css_output( $style ) {
|
||||
// Layout is processed separately by wp_render_layout_support_flag(), so we remove it before declaration generation.
|
||||
unset( $style['layout'] );
|
||||
$style = wp_normalize_state_preset_vars( $style );
|
||||
return $style;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds fallback border-style declarations for visible border declarations.
|
||||
*
|
||||
* CSS does not render border color or width unless a border style is also set.
|
||||
* State styles are emitted as stylesheet rules rather than inline styles, so
|
||||
* they cannot rely on the block-library inline-style attribute fallback rules.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @param array $declarations CSS declarations generated by the style engine.
|
||||
* @return array CSS declarations with fallback border styles applied where needed.
|
||||
*/
|
||||
function wp_get_state_declarations_with_fallback_border_styles( $declarations ) {
|
||||
if ( ! is_array( $declarations ) ) {
|
||||
return $declarations;
|
||||
}
|
||||
|
||||
$has_border_style = isset( $declarations['border-style'] ) && '' !== $declarations['border-style'];
|
||||
$has_border_color = isset( $declarations['border-color'] ) && '' !== $declarations['border-color'];
|
||||
$has_border_width = isset( $declarations['border-width'] ) && '' !== $declarations['border-width'];
|
||||
|
||||
if ( ! $has_border_style && ( $has_border_color || $has_border_width ) ) {
|
||||
$declarations['border-style'] = 'solid';
|
||||
}
|
||||
|
||||
$sides = array( 'top', 'right', 'bottom', 'left' );
|
||||
foreach ( $sides as $side ) {
|
||||
$side_style_property = "border-$side-style";
|
||||
$side_color_property = "border-$side-color";
|
||||
$side_width_property = "border-$side-width";
|
||||
|
||||
$has_side_style = isset( $declarations[ $side_style_property ] ) && '' !== $declarations[ $side_style_property ];
|
||||
$has_side_color = isset( $declarations[ $side_color_property ] ) && '' !== $declarations[ $side_color_property ];
|
||||
$has_side_width = isset( $declarations[ $side_width_property ] ) && '' !== $declarations[ $side_width_property ];
|
||||
|
||||
if ( ! $has_border_style && ! $has_side_style && ( $has_side_color || $has_side_width ) ) {
|
||||
$declarations[ $side_style_property ] = 'solid';
|
||||
}
|
||||
}
|
||||
|
||||
return $declarations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds background reset declarations to prevent gradient/solid color conflicts.
|
||||
*
|
||||
* When a state sets a solid background-color, any gradient applied to the
|
||||
* default state (via `background` shorthand or `background-image`) must be
|
||||
* explicitly cleared. Without this, the gradient image layer remains visible
|
||||
* on top of the solid hover color even when `!important` is used, because
|
||||
* `background-color` and `background-image` are separate CSS properties.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @param array $declarations CSS declarations generated by the style engine.
|
||||
* @return array CSS declarations with background resets applied where needed.
|
||||
*/
|
||||
function wp_get_state_declarations_with_background_resets( $declarations ) {
|
||||
if ( ! is_array( $declarations ) ) {
|
||||
return $declarations;
|
||||
}
|
||||
|
||||
$has_background_color = isset( $declarations['background-color'] ) && '' !== $declarations['background-color'];
|
||||
$has_background = isset( $declarations['background'] ) && '' !== $declarations['background'];
|
||||
$has_background_image = isset( $declarations['background-image'] ) && '' !== $declarations['background-image'];
|
||||
|
||||
/*
|
||||
* When the state sets a solid background-color but no gradient of its own,
|
||||
* emit `background-image: unset` to clear any gradient (whether stored as
|
||||
* the `background` shorthand or as `background-image`) that was applied to
|
||||
* the default / normal state via an inline style attribute. The declaration
|
||||
* is marked important when the state rule is registered with the style engine.
|
||||
*/
|
||||
if ( $has_background_color && ! $has_background && ! $has_background_image ) {
|
||||
$declarations['background-image'] = 'unset';
|
||||
}
|
||||
|
||||
return $declarations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds fallback dimension styles for aspectRatio and height block-support values.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @param array $state_style State style object.
|
||||
* @return array State style object with fallback dimension styles applied where needed.
|
||||
*/
|
||||
function wp_get_state_style_with_fallback_dimension_styles( $state_style ) {
|
||||
if ( ! is_array( $state_style ) ) {
|
||||
return $state_style;
|
||||
}
|
||||
|
||||
$dimensions = isset( $state_style['dimensions'] ) && is_array( $state_style['dimensions'] )
|
||||
? $state_style['dimensions']
|
||||
: array();
|
||||
|
||||
if ( empty( $dimensions ) ) {
|
||||
return $state_style;
|
||||
}
|
||||
|
||||
if ( wp_is_explicit_aspect_ratio_value( $dimensions['aspectRatio'] ?? null ) ) {
|
||||
return array_replace_recursive(
|
||||
$state_style,
|
||||
array(
|
||||
'dimensions' => array(
|
||||
'minHeight' => 'unset',
|
||||
'height' => 'unset',
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$has_min_height = isset( $dimensions['minHeight'] ) && ( is_string( $dimensions['minHeight'] ) || is_numeric( $dimensions['minHeight'] ) ) && '' !== trim( (string) $dimensions['minHeight'] );
|
||||
$has_height = isset( $dimensions['height'] ) && ( is_string( $dimensions['height'] ) || is_numeric( $dimensions['height'] ) ) && '' !== trim( (string) $dimensions['height'] );
|
||||
|
||||
if ( $has_min_height || $has_height ) {
|
||||
return array_replace_recursive(
|
||||
$state_style,
|
||||
array(
|
||||
'dimensions' => array(
|
||||
'aspectRatio' => 'unset',
|
||||
),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return $state_style;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a style fragment to a selector-keyed state style group.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @param array $groups Selector-keyed style groups.
|
||||
* @param string|null $selector Block or feature selector.
|
||||
* @param array $style Style fragment.
|
||||
*/
|
||||
function wp_add_state_style_group( &$groups, $selector, $style ) {
|
||||
$key = is_string( $selector ) ? $selector : '';
|
||||
|
||||
if ( ! isset( $groups[ $key ] ) ) {
|
||||
$groups[ $key ] = array(
|
||||
'selector' => $selector,
|
||||
'style' => array(),
|
||||
);
|
||||
}
|
||||
|
||||
$groups[ $key ]['style'] = array_replace_recursive( $groups[ $key ]['style'], $style );
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits a state style object into groups based on block feature selectors.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @param array $state_style State style object.
|
||||
* @param array $block_selectors Block selectors metadata.
|
||||
* @return array[] Selector/style groups.
|
||||
*/
|
||||
function wp_get_state_style_groups( $state_style, $block_selectors ) {
|
||||
$groups = array();
|
||||
|
||||
foreach ( $state_style as $feature => $feature_styles ) {
|
||||
$feature_selectors = $block_selectors[ $feature ] ?? null;
|
||||
|
||||
if ( is_string( $feature_selectors ) ) {
|
||||
wp_add_state_style_group(
|
||||
$groups,
|
||||
$feature_selectors,
|
||||
array( $feature => $feature_styles )
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( is_array( $feature_selectors ) && is_array( $feature_styles ) ) {
|
||||
$remaining_styles = $feature_styles;
|
||||
|
||||
foreach ( $feature_selectors as $subfeature => $subfeature_selector ) {
|
||||
if (
|
||||
'root' === $subfeature ||
|
||||
! is_string( $subfeature_selector ) ||
|
||||
! array_key_exists( $subfeature, $feature_styles )
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
wp_add_state_style_group(
|
||||
$groups,
|
||||
$subfeature_selector,
|
||||
array(
|
||||
$feature => array(
|
||||
$subfeature => $feature_styles[ $subfeature ],
|
||||
),
|
||||
)
|
||||
);
|
||||
unset( $remaining_styles[ $subfeature ] );
|
||||
}
|
||||
|
||||
if ( array() !== $remaining_styles ) {
|
||||
wp_add_state_style_group(
|
||||
$groups,
|
||||
$feature_selectors['root'] ?? ( $block_selectors['root'] ?? null ),
|
||||
array( $feature => $remaining_styles )
|
||||
);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
wp_add_state_style_group(
|
||||
$groups,
|
||||
$block_selectors['root'] ?? null,
|
||||
array( $feature => $feature_styles )
|
||||
);
|
||||
}
|
||||
|
||||
return array_values( $groups );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a style object with nested state keys removed.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @param array $state_style State style object.
|
||||
* @param array $nested_keys Keys to remove from the root style object.
|
||||
* @return array Root-only style object.
|
||||
*/
|
||||
function wp_get_root_state_style( $state_style, $nested_keys ) {
|
||||
if ( ! is_array( $state_style ) ) {
|
||||
return $state_style;
|
||||
}
|
||||
|
||||
$root_style = $state_style;
|
||||
foreach ( $nested_keys as $key ) {
|
||||
unset( $root_style[ $key ] );
|
||||
}
|
||||
|
||||
return $root_style;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates all element selectors for a block root selector.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @param string $root_selector The block root CSS selector.
|
||||
* @return string[] Element selectors keyed by element name.
|
||||
*/
|
||||
function wp_get_block_state_element_selectors( $root_selector ) {
|
||||
if ( ! is_string( $root_selector ) || '' === trim( $root_selector ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$block_selectors = wp_split_selector_list( $root_selector );
|
||||
$element_selectors = array();
|
||||
|
||||
foreach ( WP_Theme_JSON::ELEMENTS as $element_name => $element_selector ) {
|
||||
$selectors = array();
|
||||
|
||||
foreach ( $block_selectors as $block_selector ) {
|
||||
$block_selector = trim( $block_selector );
|
||||
if ( '' === $block_selector ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( $block_selector === $element_selector ) {
|
||||
$selectors = array( $element_selector );
|
||||
break;
|
||||
}
|
||||
|
||||
$selector_prefix = "$block_selector ";
|
||||
if ( ! str_contains( $element_selector, ',' ) ) {
|
||||
$selectors[] = $selector_prefix . $element_selector;
|
||||
continue;
|
||||
}
|
||||
|
||||
$prepended_selectors = array();
|
||||
foreach ( wp_split_selector_list( $element_selector ) as $selector ) {
|
||||
$prepended_selectors[] = $selector_prefix . $selector;
|
||||
}
|
||||
$selectors[] = implode( ',', $prepended_selectors );
|
||||
}
|
||||
|
||||
if ( ! empty( $selectors ) ) {
|
||||
$element_selectors[ $element_name ] = implode( ',', $selectors );
|
||||
}
|
||||
}
|
||||
|
||||
return $element_selectors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a compiled state style rule to a rule list.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @param array $css_rules Style rules.
|
||||
* @param string $state Pseudo-state selector.
|
||||
* @param string|null $selector Block, feature, or element selector.
|
||||
* @param array $style Style object.
|
||||
* @param string|null $rules_group Optional CSS grouping rule, e.g. a media query.
|
||||
*/
|
||||
function wp_add_block_state_style_rule( &$css_rules, $state, $selector, $style, $rules_group = null ) {
|
||||
if ( empty( $style ) || ! is_array( $style ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$compiled = wp_style_engine_get_styles(
|
||||
wp_normalize_state_style_for_css_output( $style )
|
||||
);
|
||||
$declarations = $compiled['declarations'] ?? array();
|
||||
$text_align = $style['typography']['textAlign'] ?? null;
|
||||
// Base text alignment is class-based, so state styles need a declaration.
|
||||
if ( is_string( $text_align ) && '' !== trim( $text_align ) ) {
|
||||
$declarations['text-align'] = $text_align;
|
||||
}
|
||||
|
||||
if ( empty( $declarations ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$css_rules[] = array(
|
||||
'state' => $state,
|
||||
'selector' => $selector,
|
||||
'declarations' => $declarations,
|
||||
);
|
||||
if ( ! empty( $rules_group ) ) {
|
||||
$css_rules[ count( $css_rules ) - 1 ]['rules_group'] = $rules_group;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds compiled state style rules, preserving the selector each rule targets.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @param array $state_styles Map of state to style array.
|
||||
* @param WP_Block_Type $block_type Block type.
|
||||
* @param string|null $rules_group Optional CSS grouping rule, e.g. a media query.
|
||||
* @return array[] State style rules.
|
||||
*/
|
||||
function wp_get_block_state_style_rules( $state_styles, $block_type, $rules_group = null ) {
|
||||
$css_rules = array();
|
||||
$block_selectors = isset( $block_type->selectors ) && is_array( $block_type->selectors )
|
||||
? $block_type->selectors
|
||||
: array();
|
||||
|
||||
foreach ( $state_styles as $state => $state_style ) {
|
||||
if ( empty( $state_style ) || ! is_array( $state_style ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ( wp_get_state_style_groups( $state_style, $block_selectors ) as $group ) {
|
||||
wp_add_block_state_style_rule(
|
||||
$css_rules,
|
||||
$state,
|
||||
$group['selector'],
|
||||
$group['style'],
|
||||
$rules_group
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $css_rules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a unique class for a set of state style rules.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @param string $block_name Block name.
|
||||
* @param array $css_rules State style rules.
|
||||
* @return string Unique class name.
|
||||
*/
|
||||
function wp_get_block_state_unique_class( $block_name, $css_rules ) {
|
||||
return 'wp-states-' . substr(
|
||||
md5(
|
||||
wp_json_encode(
|
||||
array(
|
||||
'blockName' => $block_name,
|
||||
'rules' => $css_rules,
|
||||
)
|
||||
)
|
||||
),
|
||||
0,
|
||||
8
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits a selector list by top-level commas.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @param string $selector CSS selector list.
|
||||
* @return string[] Selectors.
|
||||
*/
|
||||
function wp_split_selector_list( $selector ) {
|
||||
if ( ! str_contains( $selector, ',' ) ) {
|
||||
return array( $selector );
|
||||
}
|
||||
|
||||
$selectors = array();
|
||||
$current_selector = '';
|
||||
$parentheses_depth = 0;
|
||||
$selector_length = strlen( $selector );
|
||||
|
||||
for ( $i = 0; $i < $selector_length; $i++ ) {
|
||||
$char = $selector[ $i ];
|
||||
|
||||
if ( '(' === $char ) {
|
||||
++$parentheses_depth;
|
||||
} elseif ( ')' === $char && $parentheses_depth > 0 ) {
|
||||
--$parentheses_depth;
|
||||
} elseif ( ',' === $char && 0 === $parentheses_depth ) {
|
||||
$selectors[] = $current_selector;
|
||||
$current_selector = '';
|
||||
continue;
|
||||
}
|
||||
|
||||
$current_selector .= $char;
|
||||
}
|
||||
|
||||
$selectors[] = $current_selector;
|
||||
|
||||
return $selectors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a scoped selector from a block selector and optional pseudo-state.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @param string $base_selector Block-instance scoping selector.
|
||||
* @param string|null $block_selector Block or feature selector from metadata.
|
||||
* @param string $state Pseudo-state selector.
|
||||
* @return string Scoped selector.
|
||||
*/
|
||||
function wp_build_state_selector( $base_selector, $block_selector, $state ) {
|
||||
if ( ! is_string( $block_selector ) || '' === trim( $block_selector ) ) {
|
||||
return $base_selector . $state;
|
||||
}
|
||||
|
||||
$selectors = wp_split_selector_list( $block_selector );
|
||||
$scoped_selectors = array();
|
||||
|
||||
foreach ( $selectors as $selector ) {
|
||||
$selector = trim( $selector );
|
||||
if ( '' === $selector ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Replace only the leading block selector part (e.g. class name,
|
||||
* attribute selector, ID, or tag name) with the block instance selector.
|
||||
* Preserve anything after that prefix, including modifier classes on the
|
||||
* same element and combinators without spaces.
|
||||
*/
|
||||
if ( preg_match( '/^([.#]?[-_a-zA-Z0-9]+|\[[^\]]+\])/', $selector, $matches ) ) {
|
||||
$scoped_selectors[] = $base_selector . substr( $selector, strlen( $matches[0] ) ) . $state;
|
||||
continue;
|
||||
}
|
||||
|
||||
$scoped_selectors[] = $base_selector . $state;
|
||||
}
|
||||
|
||||
return empty( $scoped_selectors )
|
||||
? $base_selector . $state
|
||||
: implode( ', ', $scoped_selectors );
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders per-instance state styles on the frontend.
|
||||
*
|
||||
* @since 7.1.0
|
||||
*
|
||||
* @param string $block_content The block's rendered HTML.
|
||||
* @param array $block The block data including blockName and attrs.
|
||||
* @return string Modified block content with injected state styles.
|
||||
*/
|
||||
function wp_render_block_states_support( $block_content, $block ) {
|
||||
if ( empty( $block['blockName'] ) || empty( $block_content ) ) {
|
||||
return $block_content;
|
||||
}
|
||||
|
||||
$block_name = $block['blockName'];
|
||||
$block_type = WP_Block_Type_Registry::get_instance()->get_registered( $block_name );
|
||||
if ( ! $block_type ) {
|
||||
return $block_content;
|
||||
}
|
||||
|
||||
$supported_pseudo_states = WP_Theme_JSON::VALID_BLOCK_PSEUDO_SELECTORS[ $block_name ] ?? array();
|
||||
$style = $block['attrs']['style'] ?? array();
|
||||
$css_rules = array();
|
||||
$viewport_settings = wp_get_global_settings( array( 'viewport' ) );
|
||||
$responsive_media_queries = WP_Theme_JSON::get_viewport_media_queries( $viewport_settings );
|
||||
|
||||
foreach ( $supported_pseudo_states as $pseudo_state ) {
|
||||
if ( empty( $style[ $pseudo_state ] ) || ! is_array( $style[ $pseudo_state ] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$css_rules = array_merge(
|
||||
$css_rules,
|
||||
wp_get_block_state_style_rules(
|
||||
array( $pseudo_state => $style[ $pseudo_state ] ),
|
||||
$block_type
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
foreach ( $responsive_media_queries as $breakpoint => $media_query ) {
|
||||
if ( empty( $style[ $breakpoint ] ) || ! is_array( $style[ $breakpoint ] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$root_state_style = wp_get_root_state_style(
|
||||
$style[ $breakpoint ],
|
||||
array_merge( array( 'elements' ), $supported_pseudo_states )
|
||||
);
|
||||
|
||||
if ( ! empty( $root_state_style ) ) {
|
||||
$css_rules = array_merge(
|
||||
$css_rules,
|
||||
wp_get_block_state_style_rules(
|
||||
array( '' => $root_state_style ),
|
||||
$block_type,
|
||||
$media_query
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
! empty( $style[ $breakpoint ]['elements'] ) &&
|
||||
is_array( $style[ $breakpoint ]['elements'] )
|
||||
) {
|
||||
$element_selectors = wp_get_block_state_element_selectors(
|
||||
wp_get_block_css_selector( $block_type )
|
||||
);
|
||||
|
||||
foreach ( $style[ $breakpoint ]['elements'] as $element_name => $element_style ) {
|
||||
if (
|
||||
empty( $element_style ) ||
|
||||
! is_array( $element_style ) ||
|
||||
empty( $element_selectors[ $element_name ] )
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$element_pseudo_states = WP_Theme_JSON::VALID_ELEMENT_PSEUDO_SELECTORS[ $element_name ]
|
||||
?? array();
|
||||
$root_element_style = wp_get_root_state_style(
|
||||
$element_style,
|
||||
$element_pseudo_states
|
||||
);
|
||||
|
||||
wp_add_block_state_style_rule(
|
||||
$css_rules,
|
||||
'',
|
||||
$element_selectors[ $element_name ],
|
||||
$root_element_style,
|
||||
$media_query
|
||||
);
|
||||
|
||||
foreach ( $element_pseudo_states as $pseudo_state ) {
|
||||
if (
|
||||
empty( $element_style[ $pseudo_state ] ) ||
|
||||
! is_array( $element_style[ $pseudo_state ] )
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
wp_add_block_state_style_rule(
|
||||
$css_rules,
|
||||
$pseudo_state,
|
||||
$element_selectors[ $element_name ],
|
||||
$element_style[ $pseudo_state ],
|
||||
$media_query
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ( $supported_pseudo_states as $pseudo_state ) {
|
||||
if ( empty( $style[ $breakpoint ][ $pseudo_state ] ) || ! is_array( $style[ $breakpoint ][ $pseudo_state ] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$css_rules = array_merge(
|
||||
$css_rules,
|
||||
wp_get_block_state_style_rules(
|
||||
array( $pseudo_state => $style[ $breakpoint ][ $pseudo_state ] ),
|
||||
$block_type,
|
||||
$media_query
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ( empty( $css_rules ) ) {
|
||||
return $block_content;
|
||||
}
|
||||
|
||||
$unique_class = wp_get_block_state_unique_class( $block_name, $css_rules );
|
||||
|
||||
/*
|
||||
* Register each state's CSS rules with the block-supports style engine store.
|
||||
* The store deduplicates rules by selector — two block instances with identical
|
||||
* state styles share the same hash class and therefore the same selector,
|
||||
* so only one CSS rule is emitted. The store is flushed to the page by
|
||||
* wp_enqueue_stored_styles() rather than injected inline here.
|
||||
*
|
||||
* State declarations need !important to apply reliably over inline styles and
|
||||
* preset utility classes such as .has-accent-3-background-color.
|
||||
*
|
||||
* Layout-driven state styles (responsive layout, blockGap, child layout) are
|
||||
* handled by wp_render_layout_support_flag() so they share a selector with
|
||||
* the base layout and target the correct (inner) wrapper element.
|
||||
*/
|
||||
$style_rules = array();
|
||||
foreach ( $css_rules as $rule ) {
|
||||
$declarations = $rule['declarations'];
|
||||
$important_declaration_values = wp_get_state_declarations_with_background_resets( $declarations );
|
||||
$important_declarations = new WP_Style_Engine_CSS_Declarations();
|
||||
foreach ( $important_declaration_values as $property => $value ) {
|
||||
$important_declarations->add_declaration(
|
||||
$property,
|
||||
$value,
|
||||
array(
|
||||
'important' => true,
|
||||
)
|
||||
);
|
||||
}
|
||||
$selector = wp_build_state_selector(
|
||||
".$unique_class",
|
||||
$rule['selector'],
|
||||
$rule['state']
|
||||
);
|
||||
$important_style_rule = array(
|
||||
'selector' => $selector,
|
||||
'declarations' => $important_declarations,
|
||||
);
|
||||
if ( ! empty( $rule['rules_group'] ) ) {
|
||||
$important_style_rule['rules_group'] = $rule['rules_group'];
|
||||
}
|
||||
$style_rules[] = $important_style_rule;
|
||||
|
||||
$fallback_declarations = wp_get_state_declarations_with_fallback_border_styles( $declarations );
|
||||
foreach ( array_keys( $declarations ) as $property ) {
|
||||
unset( $fallback_declarations[ $property ] );
|
||||
}
|
||||
|
||||
if ( empty( $fallback_declarations ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$fallback_style_rule = array(
|
||||
'selector' => $selector,
|
||||
'declarations' => $fallback_declarations,
|
||||
);
|
||||
if ( ! empty( $rule['rules_group'] ) ) {
|
||||
$fallback_style_rule['rules_group'] = $rule['rules_group'];
|
||||
}
|
||||
$style_rules[] = $fallback_style_rule;
|
||||
}
|
||||
|
||||
wp_style_engine_get_stylesheet_from_css_rules(
|
||||
$style_rules,
|
||||
array(
|
||||
'context' => 'block-supports',
|
||||
'prettify' => false,
|
||||
)
|
||||
);
|
||||
|
||||
$processor = new WP_HTML_Tag_Processor( $block_content );
|
||||
if ( $processor->next_tag() ) {
|
||||
$processor->add_class( $unique_class );
|
||||
}
|
||||
return $processor->get_updated_html();
|
||||
}
|
||||
add_filter( 'render_block', 'wp_render_block_states_support', 10, 2 );
|
||||
@@ -0,0 +1,719 @@
|
||||
<?php
|
||||
/**
|
||||
* Typography block support flag.
|
||||
*
|
||||
* @package WordPress
|
||||
* @since 5.6.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Registers the style and typography block attributes for block types that support it.
|
||||
*
|
||||
* @since 5.6.0
|
||||
* @since 6.3.0 Added support for text-columns.
|
||||
* @since 7.0.0 Added support for text-indent.
|
||||
* @access private
|
||||
*
|
||||
* @param WP_Block_Type $block_type Block Type.
|
||||
*/
|
||||
function wp_register_typography_support( $block_type ) {
|
||||
if ( ! ( $block_type instanceof WP_Block_Type ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$typography_supports = $block_type->supports['typography'] ?? false;
|
||||
if ( ! $typography_supports ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$has_font_family_support = $typography_supports['__experimentalFontFamily'] ?? false;
|
||||
$has_font_size_support = $typography_supports['fontSize'] ?? false;
|
||||
$has_font_style_support = $typography_supports['__experimentalFontStyle'] ?? false;
|
||||
$has_font_weight_support = $typography_supports['__experimentalFontWeight'] ?? false;
|
||||
$has_letter_spacing_support = $typography_supports['__experimentalLetterSpacing'] ?? false;
|
||||
$has_line_height_support = $typography_supports['lineHeight'] ?? false;
|
||||
$has_text_align_support = $typography_supports['textAlign'] ?? false;
|
||||
$has_text_columns_support = $typography_supports['textColumns'] ?? false;
|
||||
$has_text_decoration_support = $typography_supports['__experimentalTextDecoration'] ?? false;
|
||||
$has_text_transform_support = $typography_supports['__experimentalTextTransform'] ?? false;
|
||||
$has_text_indent_support = $typography_supports['textIndent'] ?? false;
|
||||
$has_writing_mode_support = $typography_supports['__experimentalWritingMode'] ?? false;
|
||||
|
||||
$has_typography_support = $has_font_family_support
|
||||
|| $has_font_size_support
|
||||
|| $has_font_style_support
|
||||
|| $has_font_weight_support
|
||||
|| $has_letter_spacing_support
|
||||
|| $has_line_height_support
|
||||
|| $has_text_align_support
|
||||
|| $has_text_columns_support
|
||||
|| $has_text_decoration_support
|
||||
|| $has_text_transform_support
|
||||
|| $has_text_indent_support
|
||||
|| $has_writing_mode_support;
|
||||
|
||||
if ( ! $block_type->attributes ) {
|
||||
$block_type->attributes = array();
|
||||
}
|
||||
|
||||
if ( $has_typography_support && ! array_key_exists( 'style', $block_type->attributes ) ) {
|
||||
$block_type->attributes['style'] = array(
|
||||
'type' => 'object',
|
||||
);
|
||||
}
|
||||
|
||||
if ( $has_font_size_support && ! array_key_exists( 'fontSize', $block_type->attributes ) ) {
|
||||
$block_type->attributes['fontSize'] = array(
|
||||
'type' => 'string',
|
||||
);
|
||||
}
|
||||
|
||||
if ( $has_font_family_support && ! array_key_exists( 'fontFamily', $block_type->attributes ) ) {
|
||||
$block_type->attributes['fontFamily'] = array(
|
||||
'type' => 'string',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds CSS classes and inline styles for typography features such as font sizes
|
||||
* to the incoming attributes array. This will be applied to the block markup in
|
||||
* the front-end.
|
||||
*
|
||||
* @since 5.6.0
|
||||
* @since 6.1.0 Used the style engine to generate CSS and classnames.
|
||||
* @since 6.3.0 Added support for text-columns.
|
||||
* @since 7.0.0 Added support for text-indent.
|
||||
* @access private
|
||||
*
|
||||
* @param WP_Block_Type $block_type Block type.
|
||||
* @param array $block_attributes Block attributes.
|
||||
* @return array Typography CSS classes and inline styles.
|
||||
*/
|
||||
function wp_apply_typography_support( $block_type, $block_attributes ) {
|
||||
if ( ! ( $block_type instanceof WP_Block_Type ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$typography_supports = $block_type->supports['typography'] ?? false;
|
||||
if ( ! $typography_supports ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
if ( wp_should_skip_block_supports_serialization( $block_type, 'typography' ) ) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$has_font_family_support = $typography_supports['__experimentalFontFamily'] ?? false;
|
||||
$has_font_size_support = $typography_supports['fontSize'] ?? false;
|
||||
$has_font_style_support = $typography_supports['__experimentalFontStyle'] ?? false;
|
||||
$has_font_weight_support = $typography_supports['__experimentalFontWeight'] ?? false;
|
||||
$has_letter_spacing_support = $typography_supports['__experimentalLetterSpacing'] ?? false;
|
||||
$has_line_height_support = $typography_supports['lineHeight'] ?? false;
|
||||
$has_text_align_support = $typography_supports['textAlign'] ?? false;
|
||||
$has_text_columns_support = $typography_supports['textColumns'] ?? false;
|
||||
$has_text_decoration_support = $typography_supports['__experimentalTextDecoration'] ?? false;
|
||||
$has_text_transform_support = $typography_supports['__experimentalTextTransform'] ?? false;
|
||||
$has_text_indent_support = $typography_supports['textIndent'] ?? false;
|
||||
$has_writing_mode_support = $typography_supports['__experimentalWritingMode'] ?? false;
|
||||
|
||||
// Whether to skip individual block support features.
|
||||
$should_skip_font_size = wp_should_skip_block_supports_serialization( $block_type, 'typography', 'fontSize' );
|
||||
$should_skip_font_family = wp_should_skip_block_supports_serialization( $block_type, 'typography', 'fontFamily' );
|
||||
$should_skip_font_style = wp_should_skip_block_supports_serialization( $block_type, 'typography', 'fontStyle' );
|
||||
$should_skip_font_weight = wp_should_skip_block_supports_serialization( $block_type, 'typography', 'fontWeight' );
|
||||
$should_skip_line_height = wp_should_skip_block_supports_serialization( $block_type, 'typography', 'lineHeight' );
|
||||
$should_skip_text_align = wp_should_skip_block_supports_serialization( $block_type, 'typography', 'textAlign' );
|
||||
$should_skip_text_columns = wp_should_skip_block_supports_serialization( $block_type, 'typography', 'textColumns' );
|
||||
$should_skip_text_decoration = wp_should_skip_block_supports_serialization( $block_type, 'typography', 'textDecoration' );
|
||||
$should_skip_text_transform = wp_should_skip_block_supports_serialization( $block_type, 'typography', 'textTransform' );
|
||||
$should_skip_letter_spacing = wp_should_skip_block_supports_serialization( $block_type, 'typography', 'letterSpacing' );
|
||||
$should_skip_text_indent = wp_should_skip_block_supports_serialization( $block_type, 'typography', 'textIndent' );
|
||||
$should_skip_writing_mode = wp_should_skip_block_supports_serialization( $block_type, 'typography', 'writingMode' );
|
||||
|
||||
$typography_block_styles = array();
|
||||
if ( $has_font_size_support && ! $should_skip_font_size ) {
|
||||
$preset_font_size = array_key_exists( 'fontSize', $block_attributes )
|
||||
? "var:preset|font-size|{$block_attributes['fontSize']}"
|
||||
: null;
|
||||
$custom_font_size = $block_attributes['style']['typography']['fontSize'] ?? null;
|
||||
$typography_block_styles['fontSize'] = $preset_font_size ? $preset_font_size : wp_get_typography_font_size_value(
|
||||
array(
|
||||
'size' => $custom_font_size,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if ( $has_font_family_support && ! $should_skip_font_family ) {
|
||||
$preset_font_family = array_key_exists( 'fontFamily', $block_attributes )
|
||||
? "var:preset|font-family|{$block_attributes['fontFamily']}"
|
||||
: null;
|
||||
$custom_font_family = isset( $block_attributes['style']['typography']['fontFamily'] )
|
||||
? wp_typography_get_preset_inline_style_value( $block_attributes['style']['typography']['fontFamily'], 'font-family' )
|
||||
: null;
|
||||
$typography_block_styles['fontFamily'] = $preset_font_family ? $preset_font_family : $custom_font_family;
|
||||
}
|
||||
|
||||
if (
|
||||
$has_font_style_support &&
|
||||
! $should_skip_font_style &&
|
||||
isset( $block_attributes['style']['typography']['fontStyle'] )
|
||||
) {
|
||||
$typography_block_styles['fontStyle'] = wp_typography_get_preset_inline_style_value(
|
||||
$block_attributes['style']['typography']['fontStyle'],
|
||||
'font-style'
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
$has_font_weight_support &&
|
||||
! $should_skip_font_weight &&
|
||||
isset( $block_attributes['style']['typography']['fontWeight'] )
|
||||
) {
|
||||
$typography_block_styles['fontWeight'] = wp_typography_get_preset_inline_style_value(
|
||||
$block_attributes['style']['typography']['fontWeight'],
|
||||
'font-weight'
|
||||
);
|
||||
}
|
||||
|
||||
if ( $has_line_height_support && ! $should_skip_line_height ) {
|
||||
$typography_block_styles['lineHeight'] = $block_attributes['style']['typography']['lineHeight'] ?? null;
|
||||
}
|
||||
|
||||
if ( $has_text_align_support && ! $should_skip_text_align ) {
|
||||
$typography_block_styles['textAlign'] = $block_attributes['style']['typography']['textAlign'] ?? null;
|
||||
}
|
||||
|
||||
if ( $has_text_columns_support && ! $should_skip_text_columns && isset( $block_attributes['style']['typography']['textColumns'] ) ) {
|
||||
$typography_block_styles['textColumns'] = $block_attributes['style']['typography']['textColumns'] ?? null;
|
||||
}
|
||||
|
||||
if (
|
||||
$has_text_decoration_support &&
|
||||
! $should_skip_text_decoration &&
|
||||
isset( $block_attributes['style']['typography']['textDecoration'] )
|
||||
) {
|
||||
$typography_block_styles['textDecoration'] = wp_typography_get_preset_inline_style_value(
|
||||
$block_attributes['style']['typography']['textDecoration'],
|
||||
'text-decoration'
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
$has_text_transform_support &&
|
||||
! $should_skip_text_transform &&
|
||||
isset( $block_attributes['style']['typography']['textTransform'] )
|
||||
) {
|
||||
$typography_block_styles['textTransform'] = wp_typography_get_preset_inline_style_value(
|
||||
$block_attributes['style']['typography']['textTransform'],
|
||||
'text-transform'
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
$has_letter_spacing_support &&
|
||||
! $should_skip_letter_spacing &&
|
||||
isset( $block_attributes['style']['typography']['letterSpacing'] )
|
||||
) {
|
||||
$typography_block_styles['letterSpacing'] = wp_typography_get_preset_inline_style_value(
|
||||
$block_attributes['style']['typography']['letterSpacing'],
|
||||
'letter-spacing'
|
||||
);
|
||||
}
|
||||
|
||||
if ( $has_writing_mode_support &&
|
||||
! $should_skip_writing_mode &&
|
||||
isset( $block_attributes['style']['typography']['writingMode'] )
|
||||
) {
|
||||
$typography_block_styles['writingMode'] = $block_attributes['style']['typography']['writingMode'] ?? null;
|
||||
}
|
||||
|
||||
if ( $has_text_indent_support && ! $should_skip_text_indent && isset( $block_attributes['style']['typography']['textIndent'] ) ) {
|
||||
$typography_block_styles['textIndent'] = $block_attributes['style']['typography']['textIndent'] ?? null;
|
||||
}
|
||||
|
||||
$attributes = array();
|
||||
$classnames = array();
|
||||
$styles = wp_style_engine_get_styles(
|
||||
array( 'typography' => $typography_block_styles ),
|
||||
array( 'convert_vars_to_classnames' => true )
|
||||
);
|
||||
|
||||
if ( ! empty( $styles['classnames'] ) ) {
|
||||
$classnames[] = $styles['classnames'];
|
||||
}
|
||||
|
||||
if ( $has_text_align_support && ! $should_skip_text_align && isset( $block_attributes['style']['typography']['textAlign'] ) ) {
|
||||
$classnames[] = 'has-text-align-' . $block_attributes['style']['typography']['textAlign'];
|
||||
}
|
||||
|
||||
if ( ! empty( $classnames ) ) {
|
||||
$attributes['class'] = implode( ' ', $classnames );
|
||||
}
|
||||
|
||||
if ( ! empty( $styles['css'] ) ) {
|
||||
$attributes['style'] = $styles['css'];
|
||||
}
|
||||
|
||||
return $attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an inline style value for a typography feature e.g. text decoration,
|
||||
* text transform, and font style.
|
||||
*
|
||||
* Note: This function is for backwards compatibility.
|
||||
* * It is necessary to parse older blocks whose typography styles contain presets.
|
||||
* * It mostly replaces the deprecated `wp_typography_get_css_variable_inline_style()`,
|
||||
* but skips compiling a CSS declaration as the style engine takes over this role.
|
||||
* @link https://github.com/wordpress/gutenberg/pull/27555
|
||||
*
|
||||
* @since 6.1.0
|
||||
*
|
||||
* @param string $style_value A raw style value for a single typography feature from a block's style attribute.
|
||||
* @param string $css_property Slug for the CSS property the inline style sets.
|
||||
* @return string A CSS inline style value.
|
||||
*/
|
||||
function wp_typography_get_preset_inline_style_value( $style_value, $css_property ) {
|
||||
// If the style value is not a preset CSS variable go no further.
|
||||
if ( empty( $style_value ) || ! str_contains( $style_value, "var:preset|{$css_property}|" ) ) {
|
||||
return $style_value;
|
||||
}
|
||||
|
||||
/*
|
||||
* For backwards compatibility.
|
||||
* Presets were removed in WordPress/gutenberg#27555.
|
||||
* A preset CSS variable is the style.
|
||||
* Gets the style value from the string and return CSS style.
|
||||
*/
|
||||
$index_to_splice = strrpos( $style_value, '|' ) + 1;
|
||||
$slug = _wp_to_kebab_case( substr( $style_value, $index_to_splice ) );
|
||||
|
||||
// Return the actual CSS inline style value,
|
||||
// e.g. `var(--wp--preset--text-decoration--underline);`.
|
||||
return sprintf( 'var(--wp--preset--%s--%s);', $css_property, $slug );
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders typography styles/content to the block wrapper.
|
||||
*
|
||||
* @since 6.1.0
|
||||
*
|
||||
* @param string $block_content Rendered block content.
|
||||
* @param array $block Block object.
|
||||
* @return string Filtered block content.
|
||||
*/
|
||||
function wp_render_typography_support( $block_content, $block ) {
|
||||
if ( ! empty( $block['attrs']['fitText'] ) && $block['attrs']['fitText'] && ! is_admin() ) {
|
||||
wp_enqueue_script_module( '@wordpress/block-editor/utils/fit-text-frontend' );
|
||||
|
||||
// Add Interactivity API directives for fit text to work with client-side navigation.
|
||||
if ( ! empty( $block_content ) ) {
|
||||
$processor = new WP_HTML_Tag_Processor( $block_content );
|
||||
if ( $processor->next_tag() ) {
|
||||
if ( ! $processor->get_attribute( 'data-wp-interactive' ) ) {
|
||||
$processor->set_attribute( 'data-wp-interactive', true );
|
||||
}
|
||||
$processor->set_attribute( 'data-wp-context---core-fit-text', 'core/fit-text::{"fontSize":""}' );
|
||||
$processor->set_attribute( 'data-wp-init---core-fit-text', 'core/fit-text::callbacks.init' );
|
||||
$processor->set_attribute( 'data-wp-style--font-size', 'core/fit-text::context.fontSize' );
|
||||
$block_content = $processor->get_updated_html();
|
||||
}
|
||||
}
|
||||
// fitText supersedes any other typography features
|
||||
return $block_content;
|
||||
}
|
||||
if ( ! isset( $block['attrs']['style']['typography']['fontSize'] ) ) {
|
||||
return $block_content;
|
||||
}
|
||||
|
||||
$custom_font_size = $block['attrs']['style']['typography']['fontSize'];
|
||||
$fluid_font_size = wp_get_typography_font_size_value( array( 'size' => $custom_font_size ) );
|
||||
|
||||
/*
|
||||
* Checks that $fluid_font_size does not match $custom_font_size,
|
||||
* which means it's been mutated by the fluid font size functions.
|
||||
*/
|
||||
if ( ! empty( $fluid_font_size ) && $fluid_font_size !== $custom_font_size ) {
|
||||
// Replaces the first instance of `font-size:$custom_font_size` with `font-size:$fluid_font_size`.
|
||||
return preg_replace( '/font-size\s*:\s*' . preg_quote( $custom_font_size, '/' ) . '\s*;?/', 'font-size:' . esc_attr( $fluid_font_size ) . ';', $block_content, 1 );
|
||||
}
|
||||
|
||||
return $block_content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks a string for a unit and value and returns an array
|
||||
* consisting of `'value'` and `'unit'`, e.g. array( '42', 'rem' ).
|
||||
*
|
||||
* @since 6.1.0
|
||||
*
|
||||
* @param string|int|float $raw_value Raw size value from theme.json.
|
||||
* @param array $options {
|
||||
* Optional. An associative array of options. Default is empty array.
|
||||
*
|
||||
* @type string $coerce_to Coerce the value to rem or px. Default `'rem'`.
|
||||
* @type int $root_size_value Value of root font size for rem|em <-> px conversion. Default `16`.
|
||||
* @type string[] $acceptable_units An array of font size units. Default `array( 'rem', 'px', 'em' )`;
|
||||
* }
|
||||
* @return array|null An array consisting of `'value'` and `'unit'` properties on success.
|
||||
* `null` on failure.
|
||||
* @phpstan-param array{
|
||||
* coerce_to?: string,
|
||||
* root_size_value?: positive-int,
|
||||
* acceptable_units?: non-empty-array<non-empty-string>,
|
||||
* } $options
|
||||
* @phpstan-return array{ value: float, unit: non-empty-string }|null
|
||||
*/
|
||||
function wp_get_typography_value_and_unit( $raw_value, $options = array() ): ?array {
|
||||
if ( ! is_string( $raw_value ) && ! is_int( $raw_value ) && ! is_float( $raw_value ) ) {
|
||||
_doing_it_wrong(
|
||||
__FUNCTION__,
|
||||
__( 'Raw size value must be a string, integer, or float.' ),
|
||||
'6.1.0'
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( empty( $raw_value ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Converts numbers to pixel values by default.
|
||||
if ( is_numeric( $raw_value ) ) {
|
||||
$raw_value = $raw_value . 'px';
|
||||
}
|
||||
|
||||
$defaults = array(
|
||||
'coerce_to' => '',
|
||||
'root_size_value' => 16,
|
||||
'acceptable_units' => array( 'rem', 'px', 'em' ),
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array{
|
||||
* coerce_to: string,
|
||||
* root_size_value: positive-int,
|
||||
* acceptable_units: non-empty-array<non-empty-string>,
|
||||
* } $options
|
||||
*/
|
||||
$options = wp_parse_args( $options, $defaults );
|
||||
|
||||
// Bails out if the raw value can't be parsed.
|
||||
if ( ! preg_match( '/^(\d*\.?\d+)([a-zA-Z]+|%)$/', $raw_value, $matches ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$value = (float) $matches[1];
|
||||
$unit = $matches[2];
|
||||
|
||||
if ( ! in_array( $unit, $options['acceptable_units'], true ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* Default browser font size. Later, possibly could inject some JS to
|
||||
* compute this `getComputedStyle( document.querySelector( "html" ) ).fontSize`.
|
||||
*/
|
||||
if ( 'px' === $options['coerce_to'] && ( 'em' === $unit || 'rem' === $unit ) ) {
|
||||
$value = $value * $options['root_size_value'];
|
||||
$unit = $options['coerce_to'];
|
||||
}
|
||||
|
||||
if ( 'px' === $unit && ( 'em' === $options['coerce_to'] || 'rem' === $options['coerce_to'] ) ) {
|
||||
$value = $value / $options['root_size_value'];
|
||||
$unit = $options['coerce_to'];
|
||||
}
|
||||
|
||||
/*
|
||||
* No calculation is required if swapping between em and rem yet,
|
||||
* since we assume a root size value. Later we might like to differentiate between
|
||||
* :root font size (rem) and parent element font size (em) relativity.
|
||||
*/
|
||||
if ( ( 'em' === $options['coerce_to'] || 'rem' === $options['coerce_to'] ) && ( 'em' === $unit || 'rem' === $unit ) ) {
|
||||
$unit = $options['coerce_to'];
|
||||
}
|
||||
|
||||
return array(
|
||||
'value' => round( $value, 3 ),
|
||||
'unit' => $unit,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal implementation of CSS clamp() based on available min/max viewport
|
||||
* width and min/max font sizes.
|
||||
*
|
||||
* @since 6.1.0
|
||||
* @since 6.3.0 Checks for unsupported min/max viewport values that cause invalid clamp values.
|
||||
* @since 6.5.0 Returns early when min and max viewport subtraction is zero to avoid division by zero.
|
||||
* @access private
|
||||
*
|
||||
* @param array $args {
|
||||
* Optional. An associative array of values to calculate a fluid formula
|
||||
* for font size. Default is empty array.
|
||||
*
|
||||
* @type string $maximum_viewport_width Maximum size up to which type will have fluidity.
|
||||
* @type string $minimum_viewport_width Minimum viewport size from which type will have fluidity.
|
||||
* @type string $maximum_font_size Maximum font size for any clamp() calculation.
|
||||
* @type string $minimum_font_size Minimum font size for any clamp() calculation.
|
||||
* @type int $scale_factor A scale factor to determine how fast a font scales within boundaries.
|
||||
* }
|
||||
* @return string|null A font-size value using clamp() on success, otherwise null.
|
||||
*/
|
||||
function wp_get_computed_fluid_typography_value( $args = array() ) {
|
||||
$maximum_viewport_width_raw = $args['maximum_viewport_width'] ?? null;
|
||||
$minimum_viewport_width_raw = $args['minimum_viewport_width'] ?? null;
|
||||
$maximum_font_size_raw = $args['maximum_font_size'] ?? null;
|
||||
$minimum_font_size_raw = $args['minimum_font_size'] ?? null;
|
||||
$scale_factor = $args['scale_factor'] ?? null;
|
||||
|
||||
// Normalizes the minimum font size in order to use the value for calculations.
|
||||
$minimum_font_size = wp_get_typography_value_and_unit( $minimum_font_size_raw );
|
||||
|
||||
/*
|
||||
* We get a 'preferred' unit to keep units consistent when calculating,
|
||||
* otherwise the result will not be accurate.
|
||||
*/
|
||||
$font_size_unit = $minimum_font_size['unit'] ?? 'rem';
|
||||
|
||||
// Normalizes the maximum font size in order to use the value for calculations.
|
||||
$maximum_font_size = wp_get_typography_value_and_unit(
|
||||
$maximum_font_size_raw,
|
||||
array(
|
||||
'coerce_to' => $font_size_unit,
|
||||
)
|
||||
);
|
||||
|
||||
// Checks for mandatory min and max sizes, and protects against unsupported units.
|
||||
if ( ! $maximum_font_size || ! $minimum_font_size ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Uses rem for accessible fluid target font scaling.
|
||||
$minimum_font_size_rem = wp_get_typography_value_and_unit(
|
||||
$minimum_font_size_raw,
|
||||
array(
|
||||
'coerce_to' => 'rem',
|
||||
)
|
||||
);
|
||||
|
||||
// Viewport widths defined for fluid typography. Normalize units.
|
||||
$maximum_viewport_width = wp_get_typography_value_and_unit(
|
||||
$maximum_viewport_width_raw,
|
||||
array(
|
||||
'coerce_to' => $font_size_unit,
|
||||
)
|
||||
);
|
||||
$minimum_viewport_width = wp_get_typography_value_and_unit(
|
||||
$minimum_viewport_width_raw,
|
||||
array(
|
||||
'coerce_to' => $font_size_unit,
|
||||
)
|
||||
);
|
||||
|
||||
// Protects against unsupported units in min and max viewport widths.
|
||||
if ( ! $minimum_viewport_width || ! $maximum_viewport_width ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Calculates the linear factor denominator. If it's 0, we cannot calculate a fluid value.
|
||||
$linear_factor_denominator = $maximum_viewport_width['value'] - $minimum_viewport_width['value'];
|
||||
if ( empty( $linear_factor_denominator ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* Build CSS rule.
|
||||
* Borrowed from https://websemantics.uk/tools/responsive-font-calculator/.
|
||||
*/
|
||||
$view_port_width_offset = round( $minimum_viewport_width['value'] / 100, 3 ) . $font_size_unit;
|
||||
$linear_factor = 100 * ( ( $maximum_font_size['value'] - $minimum_font_size['value'] ) / ( $linear_factor_denominator ) );
|
||||
$linear_factor_scaled = round( $linear_factor * $scale_factor, 3 );
|
||||
$linear_factor_scaled = empty( $linear_factor_scaled ) ? 1 : $linear_factor_scaled;
|
||||
$fluid_target_font_size = implode( '', $minimum_font_size_rem ) . " + ((1vw - $view_port_width_offset) * $linear_factor_scaled)";
|
||||
|
||||
return "clamp($minimum_font_size_raw, $fluid_target_font_size, $maximum_font_size_raw)";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a font-size value based on a given font-size preset.
|
||||
* Takes into account fluid typography parameters and attempts to return a CSS
|
||||
* formula depending on available, valid values.
|
||||
*
|
||||
* @since 6.1.0
|
||||
* @since 6.1.1 Adjusted rules for min and max font sizes.
|
||||
* @since 6.2.0 Added 'settings.typography.fluid.minFontSize' support.
|
||||
* @since 6.3.0 Using layout.wideSize as max viewport width, and logarithmic scale factor to calculate minimum font scale.
|
||||
* @since 6.4.0 Added configurable min and max viewport width values to the typography.fluid theme.json schema.
|
||||
* @since 6.6.0 Deprecated bool argument $should_use_fluid_typography.
|
||||
* @since 6.7.0 Font size presets can enable fluid typography individually, even if it’s disabled globally.
|
||||
*
|
||||
* @param array $preset {
|
||||
* Required. fontSizes preset value as seen in theme.json.
|
||||
*
|
||||
* @type string $name Name of the font size preset.
|
||||
* @type string $slug Kebab-case, unique identifier for the font size preset.
|
||||
* @type string|int|float $size CSS font-size value, including units if applicable.
|
||||
* }
|
||||
* @param bool|array $settings Optional Theme JSON settings array that overrides any global theme settings.
|
||||
* Default is false.
|
||||
* @return string|null Font-size value or null if a size is not passed in $preset.
|
||||
*/
|
||||
|
||||
|
||||
function wp_get_typography_font_size_value( $preset, $settings = array() ) {
|
||||
if ( ! isset( $preset['size'] ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* Catches falsy values and 0/'0'. Fluid calculations cannot be performed on `0`.
|
||||
* Also returns early when a preset font size explicitly disables fluid typography with `false`.
|
||||
*/
|
||||
$fluid_font_size_settings = $preset['fluid'] ?? null;
|
||||
if ( false === $fluid_font_size_settings || empty( $preset['size'] ) ) {
|
||||
return $preset['size'];
|
||||
}
|
||||
|
||||
/*
|
||||
* As a boolean (deprecated since 6.6), $settings acts as an override to switch fluid typography "on" (`true`) or "off" (`false`).
|
||||
*/
|
||||
if ( is_bool( $settings ) ) {
|
||||
_deprecated_argument( __FUNCTION__, '6.6.0', __( '`boolean` type for second argument `$settings` is deprecated. Use `array()` instead.' ) );
|
||||
$settings = array(
|
||||
'typography' => array(
|
||||
'fluid' => $settings,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Fallback to global settings as default.
|
||||
$global_settings = wp_get_global_settings();
|
||||
$settings = wp_parse_args(
|
||||
$settings,
|
||||
$global_settings
|
||||
);
|
||||
|
||||
$typography_settings = $settings['typography'] ?? array();
|
||||
|
||||
/*
|
||||
* Return early when fluid typography is disabled in the settings, and there
|
||||
* are no local settings to enable it for the individual preset.
|
||||
*
|
||||
* If this condition isn't met, either the settings or individual preset settings
|
||||
* have enabled fluid typography.
|
||||
*/
|
||||
if ( empty( $typography_settings['fluid'] ) && empty( $fluid_font_size_settings ) ) {
|
||||
return $preset['size'];
|
||||
}
|
||||
|
||||
$fluid_settings = $typography_settings['fluid'] ?? array();
|
||||
$layout_settings = $settings['layout'] ?? array();
|
||||
|
||||
// Defaults.
|
||||
$default_maximum_viewport_width = '1600px';
|
||||
$default_minimum_viewport_width = '320px';
|
||||
$default_minimum_font_size_factor_max = 0.75;
|
||||
$default_minimum_font_size_factor_min = 0.25;
|
||||
$default_scale_factor = 1;
|
||||
$default_minimum_font_size_limit = '14px';
|
||||
|
||||
// Defaults overrides.
|
||||
$minimum_viewport_width = $fluid_settings['minViewportWidth'] ?? $default_minimum_viewport_width;
|
||||
$maximum_viewport_width = isset( $layout_settings['wideSize'] ) && ! empty( wp_get_typography_value_and_unit( $layout_settings['wideSize'] ) ) ? $layout_settings['wideSize'] : $default_maximum_viewport_width;
|
||||
if ( isset( $fluid_settings['maxViewportWidth'] ) ) {
|
||||
$maximum_viewport_width = $fluid_settings['maxViewportWidth'];
|
||||
}
|
||||
$has_min_font_size = isset( $fluid_settings['minFontSize'] ) && ! empty( wp_get_typography_value_and_unit( $fluid_settings['minFontSize'] ) );
|
||||
$minimum_font_size_limit = $has_min_font_size ? $fluid_settings['minFontSize'] : $default_minimum_font_size_limit;
|
||||
|
||||
// Try to grab explicit min and max fluid font sizes.
|
||||
$minimum_font_size_raw = $fluid_font_size_settings['min'] ?? null;
|
||||
$maximum_font_size_raw = $fluid_font_size_settings['max'] ?? null;
|
||||
|
||||
// Font sizes.
|
||||
$preferred_size = wp_get_typography_value_and_unit( $preset['size'] );
|
||||
|
||||
// Protects against unsupported units.
|
||||
if ( empty( $preferred_size['unit'] ) ) {
|
||||
return $preset['size'];
|
||||
}
|
||||
|
||||
/*
|
||||
* Normalizes the minimum font size limit according to the incoming unit,
|
||||
* in order to perform comparative checks.
|
||||
*/
|
||||
$minimum_font_size_limit = wp_get_typography_value_and_unit(
|
||||
$minimum_font_size_limit,
|
||||
array(
|
||||
'coerce_to' => $preferred_size['unit'],
|
||||
)
|
||||
);
|
||||
|
||||
// Don't enforce minimum font size if a font size has explicitly set a min and max value.
|
||||
if ( ! empty( $minimum_font_size_limit ) && ( ! $minimum_font_size_raw && ! $maximum_font_size_raw ) ) {
|
||||
/*
|
||||
* If a minimum size was not passed to this function
|
||||
* and the user-defined font size is lower than $minimum_font_size_limit,
|
||||
* do not calculate a fluid value.
|
||||
*/
|
||||
if ( $preferred_size['value'] <= $minimum_font_size_limit['value'] ) {
|
||||
return $preset['size'];
|
||||
}
|
||||
}
|
||||
|
||||
// If no fluid max font size is available use the incoming value.
|
||||
if ( ! $maximum_font_size_raw ) {
|
||||
$maximum_font_size_raw = $preferred_size['value'] . $preferred_size['unit'];
|
||||
}
|
||||
|
||||
/*
|
||||
* If no minimumFontSize is provided, create one using
|
||||
* the given font size multiplied by the min font size scale factor.
|
||||
*/
|
||||
if ( ! $minimum_font_size_raw ) {
|
||||
$preferred_font_size_in_px = 'px' === $preferred_size['unit'] ? $preferred_size['value'] : $preferred_size['value'] * 16;
|
||||
|
||||
/*
|
||||
* The scale factor is a multiplier that affects how quickly the curve will move towards the minimum,
|
||||
* that is, how quickly the size factor reaches 0 given increasing font size values.
|
||||
* For a - b * log2(), lower values of b will make the curve move towards the minimum faster.
|
||||
* The scale factor is constrained between min and max values.
|
||||
*/
|
||||
$minimum_font_size_factor = clamp( 1 - 0.075 * log( $preferred_font_size_in_px, 2 ), $default_minimum_font_size_factor_min, $default_minimum_font_size_factor_max );
|
||||
$calculated_minimum_font_size = round( $preferred_size['value'] * $minimum_font_size_factor, 3 );
|
||||
|
||||
// Only use calculated min font size if it's > $minimum_font_size_limit value.
|
||||
if ( ! empty( $minimum_font_size_limit ) && $calculated_minimum_font_size <= $minimum_font_size_limit['value'] ) {
|
||||
$minimum_font_size_raw = $minimum_font_size_limit['value'] . $minimum_font_size_limit['unit'];
|
||||
} else {
|
||||
$minimum_font_size_raw = $calculated_minimum_font_size . $preferred_size['unit'];
|
||||
}
|
||||
}
|
||||
|
||||
$fluid_font_size_value = wp_get_computed_fluid_typography_value(
|
||||
array(
|
||||
'minimum_viewport_width' => $minimum_viewport_width,
|
||||
'maximum_viewport_width' => $maximum_viewport_width,
|
||||
'minimum_font_size' => $minimum_font_size_raw,
|
||||
'maximum_font_size' => $maximum_font_size_raw,
|
||||
'scale_factor' => $default_scale_factor,
|
||||
)
|
||||
);
|
||||
|
||||
if ( ! empty( $fluid_font_size_value ) ) {
|
||||
return $fluid_font_size_value;
|
||||
}
|
||||
|
||||
return $preset['size'];
|
||||
}
|
||||
|
||||
// Register the block support.
|
||||
WP_Block_Supports::get_instance()->register(
|
||||
'typography',
|
||||
array(
|
||||
'register_attribute' => 'wp_register_typography_support',
|
||||
'apply' => 'wp_apply_typography_support',
|
||||
)
|
||||
);
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
/**
|
||||
* Block support utility functions.
|
||||
*
|
||||
* @package WordPress
|
||||
* @subpackage Block Supports
|
||||
* @since 6.0.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Checks whether serialization of the current block's supported properties
|
||||
* should occur.
|
||||
*
|
||||
* @since 6.0.0
|
||||
* @access private
|
||||
*
|
||||
* @param WP_Block_Type $block_type Block type.
|
||||
* @param string $feature_set Name of block support feature set..
|
||||
* @param string $feature Optional name of individual feature to check.
|
||||
*
|
||||
* @return bool Whether to serialize block support styles & classes.
|
||||
*/
|
||||
function wp_should_skip_block_supports_serialization( $block_type, $feature_set, $feature = null ) {
|
||||
if ( ! is_object( $block_type ) || ! $feature_set ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$path = array( $feature_set, '__experimentalSkipSerialization' );
|
||||
$skip_serialization = _wp_array_get( $block_type->supports, $path, false );
|
||||
|
||||
if ( is_array( $skip_serialization ) ) {
|
||||
return in_array( $feature, $skip_serialization, true );
|
||||
}
|
||||
|
||||
return $skip_serialization;
|
||||
}
|
||||
Reference in New Issue
Block a user