Skip to content

Commit

Permalink
Initial commit:
Browse files Browse the repository at this point in the history
- Migration of the style engine classes and unit tests from Gutenberg to Core.
  • Loading branch information
ramonjd committed Sep 6, 2022
1 parent 225b6cb commit e8ce7a6
Show file tree
Hide file tree
Showing 12 changed files with 2,794 additions and 0 deletions.
144 changes: 144 additions & 0 deletions src/wp-includes/style-engine.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
<?php
/**
* Style engine: Public functions
*
* This file contains a variety of public functions developers can use to interact with
* the Style Engine API.
*
* @package WordPress
* @subpackage StyleEngine
* @since 6.1.0
*/


/**
* Global public interface method to generate styles from a single style object, e.g.,
* the value of a block's attributes.style object or the top level styles in theme.json.
* See: https://developer.wordpress.org/block-editor/reference-guides/theme-json-reference/theme-json-living/#styles and
* https://developer.wordpress.org/block-editor/reference-guides/block-api/block-supports/
*
* Example usage:
*
* $styles = wp_style_engine_get_styles( array( 'color' => array( 'text' => '#cccccc' ) ) );
* // Returns `array( 'css' => 'color: #cccccc', 'declarations' => array( 'color' => '#cccccc' ), 'classnames' => 'has-color' )`.
*
* @access public
* @since 6.1.0
*
* @param array $block_styles The style object.
* @param array<string|boolean> $options array(
* 'context' => (string|null) An identifier describing the origin of the style object, e.g., 'block-supports' or 'global-styles'. Default is 'block-supports'.
* When set, the style engine will attempt to store the CSS rules, where a selector is also passed.
* 'convert_vars_to_classnames' => (boolean) Whether to skip converting CSS var:? values to var( --wp--preset--* ) values. Default is `false`.
* 'selector' => (string) When a selector is passed, `generate()` will return a full CSS rule `$selector { ...rules }`, otherwise a concatenated string of properties and values.
* );.
*
* @return array<string|array> array(
* 'css' => (string) A CSS ruleset or declarations block formatted to be placed in an HTML `style` attribute or tag.
* 'declarations' => (array) An array of property/value pairs representing parsed CSS declarations.
* 'classnames' => (string) Classnames separated by a space.
* );
*/
function wp_style_engine_get_styles( $block_styles, $options = array() ) {
if ( ! class_exists( 'WP_Style_Engine' ) ) {
return array();
}

$options = wp_parse_args(
$options,
array(
'selector' => null,
'context' => null,
'convert_vars_to_classnames' => false,
)
);

$parsed_styles = WP_Style_Engine::parse_block_styles( $block_styles, $options );

// Output.
$styles_output = array();

if ( ! empty( $parsed_styles['declarations'] ) ) {
$styles_output['css'] = WP_Style_Engine::compile_css( $parsed_styles['declarations'], $options['selector'] );
$styles_output['declarations'] = $parsed_styles['declarations'];
if ( ! empty( $options['context'] ) ) {
WP_Style_Engine::store_css_rule( $options['context'], $options['selector'], $parsed_styles['declarations'] );
}
}

if ( ! empty( $parsed_styles['classnames'] ) ) {
$styles_output['classnames'] = implode( ' ', array_unique( $parsed_styles['classnames'] ) );
}

return array_filter( $styles_output );
}

/**
* Returns compiled CSS from a collection of selectors and declarations.
* This won't add to any store, but is useful for returning a compiled style sheet from any CSS selector + declarations combos.
*
* @access public
* @since 6.1.0
*
* @param array<array> $css_rules array(
* array(
* 'selector' => (string) A CSS selector.
* declarations' => (boolean) An array of CSS definitions, e.g., array( "$property" => "$value" ).
* )
* );.
* @param array<string> $options array(
* 'context' => (string|null) An identifier describing the origin of the style object, e.g., 'block-supports' or 'global-styles'. Default is 'block-supports'.
* When set, the style engine will attempt to store the CSS rules.
* );.
*
* @return string A compiled CSS string.
*/
function wp_style_engine_get_stylesheet_from_css_rules( $css_rules, $options = array() ) {
if ( ! class_exists( 'WP_Style_Engine' ) || empty( $css_rules ) ) {
return '';
}

$options = wp_parse_args(
$options,
array(
'context' => null,
)
);

$css_rule_objects = array();
foreach ( $css_rules as $css_rule ) {
if ( empty( $css_rule['selector'] ) || empty( $css_rule['declarations'] ) || ! is_array( $css_rule['declarations'] ) ) {
continue;
}

if ( ! empty( $options['context'] ) ) {
WP_Style_Engine::store_css_rule( $options['context'], $css_rule['selector'], $css_rule['declarations'] );
}

$css_rule_objects[] = new WP_Style_Engine_CSS_Rule( $css_rule['selector'], $css_rule['declarations'] );
}

if ( empty( $css_rule_objects ) ) {
return '';
}

return WP_Style_Engine::compile_stylesheet_from_css_rules( $css_rule_objects );
}

/**
* Returns compiled CSS from a store, if found.
*
* @access public
* @since 6.1.0
*
* @param string $store_name A valid store name.
*
* @return string A compiled CSS string.
*/
function wp_style_engine_get_stylesheet_from_context( $store_name ) {
if ( ! class_exists( 'WP_Style_Engine' ) || empty( $store_name ) ) {
return '';
}

return WP_Style_Engine::compile_stylesheet_from_css_rules( WP_Style_Engine::get_store( $store_name )->get_all_rules() );
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
<?php
/**
* WP_Style_Engine_CSS_Declarations
*
* Holds, sanitizes and prints CSS rules declarations
*
* @package WordPress
* @subpackage StyleEngine
* @since 6.1.0
*/

/**
* Class WP_Style_Engine_CSS_Declarations.
*
* Holds, sanitizes, processes and prints CSS declarations for the style engine.
*
* @access private
* @since 6.1.0
*/
class WP_Style_Engine_CSS_Declarations {

/**
* An array of CSS declarations (property => value pairs).
*
* @since 6.1.0
*
* @var array
*/
protected $declarations = array();

/**
* Constructor for this object.
*
* If a `$declarations` array is passed, it will be used to populate
* the initial $declarations prop of the object by calling add_declarations().
*
* @since 6.1.0
*
* @param array $declarations An array of declarations (property => value pairs).
*/
public function __construct( $declarations = array() ) {
$this->add_declarations( $declarations );
}

/**
* Add a single declaration.
*
* @since 6.1.0
*
* @param string $property The CSS property.
* @param string $value The CSS value.
*
* @return WP_Style_Engine_CSS_Declarations Returns the object to allow chaining methods.
*/
public function add_declaration( $property, $value ) {

// Sanitize the property.
$property = $this->sanitize_property( $property );
// Bail early if the property is empty.
if ( empty( $property ) ) {
return $this;
}

// Trim the value. If empty, bail early.
$value = trim( $value );
if ( '' === $value ) {
return $this;
}

// Add the declaration property/value pair.
$this->declarations[ $property ] = $value;

return $this;
}

/**
* Remove a single declaration.
*
* @since 6.1.0
*
* @param string $property The CSS property.
*
* @return WP_Style_Engine_CSS_Declarations Returns the object to allow chaining methods.
*/
public function remove_declaration( $property ) {
unset( $this->declarations[ $property ] );
return $this;
}

/**
* Add multiple declarations.
*
* @since 6.1.0
*
* @param array $declarations An array of declarations.
*
* @return WP_Style_Engine_CSS_Declarations Returns the object to allow chaining methods.
*/
public function add_declarations( $declarations ) {
foreach ( $declarations as $property => $value ) {
$this->add_declaration( $property, $value );
}
return $this;
}

/**
* Remove multiple declarations.
*
* @since 6.1.0
*
* @param array $properties An array of properties.
*
* @return WP_Style_Engine_CSS_Declarations Returns the object to allow chaining methods.
*/
public function remove_declarations( $properties = array() ) {
foreach ( $properties as $property ) {
$this->remove_declaration( $property );
}
return $this;
}

/**
* Get the declarations array.
*
* @since 6.1.0
*
* @return array
*/
public function get_declarations() {
return $this->declarations;
}

/**
* Filters a CSS property + value pair.
*
* @since 6.1.0
*
* @param string $property The CSS property.
* @param string $value The value to be filtered.
* @param string $spacer The spacer between the colon and the value. Defaults to an empty string.
*
* @return string The filtered declaration as a single string.
*/
protected static function filter_declaration( $property, $value, $spacer = '' ) {
$filtered_value = wp_strip_all_tags( $value, true );
if ( '' !== $filtered_value ) {
return safecss_filter_attr( "{$property}:{$spacer}{$filtered_value}" );
}
return '';
}

/**
* Filters and compiles the CSS declarations.
*
* @since 6.1.0
*
* @param boolean $should_prettify Whether to add spacing, new lines and indents.
* @param number $indent_count The number of tab indents to apply to the rule. Applies if `prettify` is `true`.
*
* @return string The CSS declarations.
*/
public function get_declarations_string( $should_prettify = false, $indent_count = 0 ) {
$declarations_array = $this->get_declarations();
$declarations_output = '';
$indent = $should_prettify ? str_repeat( "\t", $indent_count ) : '';
$suffix = $should_prettify ? ' ' : '';
$suffix = $should_prettify && $indent_count > 0 ? "\n" : $suffix;
$spacer = $should_prettify ? ' ' : '';

foreach ( $declarations_array as $property => $value ) {
$filtered_declaration = static::filter_declaration( $property, $value, $spacer );
if ( $filtered_declaration ) {
$declarations_output .= "{$indent}{$filtered_declaration};$suffix";
}
}
return rtrim( $declarations_output );
}

/**
* Sanitize property names.
*
* @since 6.1.0
*
* @param string $property The CSS property.
*
* @return string The sanitized property name.
*/
protected function sanitize_property( $property ) {
return sanitize_key( $property );
}
}
Loading

0 comments on commit e8ce7a6

Please sign in to comment.