Skip to content
132 changes: 130 additions & 2 deletions src/wp-includes/block-supports/block-visibility.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@
*/

/**
* Render nothing if the block is hidden.
* Render nothing if the block is hidden, or add viewport visibility styles.
*
* @since 6.9.0
* @since 7.0.0 Added support for breakpoint visibility.
* @access private
*
* @param string $block_content Rendered block content.
Expand All @@ -23,10 +24,137 @@ function wp_render_block_visibility_support( $block_content, $block ) {
return $block_content;
}

if ( isset( $block['attrs']['metadata']['blockVisibility'] ) && false === $block['attrs']['metadata']['blockVisibility'] ) {
$block_visibility = $block['attrs']['metadata']['blockVisibility'] ?? null;

if ( false === $block_visibility ) {
return '';
}

if ( is_array( $block_visibility ) && ! empty( $block_visibility ) ) {
/*
* Breakpoints definitions are in several places in WordPress packages.
* The following are taken from: https://github.com/WordPress/gutenberg/blob/trunk/packages/base-styles/_breakpoints.scss
* The array is in a future, potential JSON format, and will be centralized
* as the feature is developed.
*
* Breakpoints as array items are defined sequentially. The first item's size is the max value.
* Each subsequent item's min is calc(previous size + 1px), and its size is the max.
* The last item's min is previous size plus 1px, and it has no max.
*/
$breakpoints = array(
array(
'name' => 'Mobile',
'slug' => 'mobile',
'size' => '480px',
),
array(
'name' => 'Tablet',
'slug' => 'tablet',
'size' => '782px',
),
array(
'name' => 'Desktop',
'slug' => 'desktop',
'size' => '960px',
),
);

/*
* Build media queries from breakpoint definitions.
* Could be absorbed into the style engine,
* as well as classname building, and declaration of the display property, if required.
*/
$breakpoint_queries = array();
$previous_size = null;
foreach ( $breakpoints as $index => $breakpoint ) {
$slug = $breakpoint['slug'];
$size = $breakpoint['size'];
$query_parts = array();

// First item: max = size.
if ( 0 === $index ) {
$query_parts[] = '(max-width: ' . $size . ')';
} elseif ( count( $breakpoints ) - 1 === $index ) {
// Last item: min = calc(previous size + 1px), no max.
$query_parts[] = '(min-width: calc(' . $previous_size . ' + 1px))';
} else {
// Middle items: min = calc(previous size + 1px), max = size.
$query_parts[] = '(min-width: calc(' . $previous_size . ' + 1px))';
$query_parts[] = '(max-width: ' . $size . ')';
}

if ( ! empty( $query_parts ) ) {
$breakpoint_queries[ $slug ] = '@media ' . implode( ' and ', $query_parts );
}

$previous_size = $size;
}

$hidden_on = array();

// Collect which breakpoints the block is hidden on (only known breakpoints).
foreach ( $block_visibility as $breakpoint => $is_visible ) {
if ( false === $is_visible && isset( $breakpoint_queries[ $breakpoint ] ) ) {
$hidden_on[] = $breakpoint;
}
}

// If no breakpoints have visibility set to false, return unchanged.
if ( empty( $hidden_on ) ) {
return $block_content;
}

/*
* If the block is hidden on all breakpoints,
* do not render the block. If these values ever become user-defined,
* we might need to output the CSS regardless of the breakpoint count.
* For example, if there is one breakpoint defined and it's hidden.
*/
if ( count( $hidden_on ) === count( $breakpoint_queries ) ) {
return '';
}

// Maintain consistent order of breakpoints for class name generation.
sort( $hidden_on );

$css_rules = array();
$class_names = array();

foreach ( $hidden_on as $breakpoint ) {
/*
* If these values ever become user-defined,
* they should be sanitized and kebab-cased.
*/
$visibility_class = 'wp-block-hidden-' . $breakpoint;
$class_names[] = $visibility_class;
$css_rules[] = array(
'selector' => '.' . $visibility_class,
'declarations' => array(
'display' => 'none !important',
),
'rules_group' => $breakpoint_queries[ $breakpoint ],
);
}

if ( ! empty( $css_rules ) ) {
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 ) );
$block_content = $processor->get_updated_html();
}
}
}
}

return $block_content;
}

Expand Down
2 changes: 2 additions & 0 deletions src/wp-includes/kses.php
Original file line number Diff line number Diff line change
Expand Up @@ -2708,6 +2708,8 @@ function safecss_filter_attr( $css, $deprecated = '' ) {
'column-span',
'column-width',

'display',

'color',
'filter',
'font',
Expand Down
Loading
Loading