-
Notifications
You must be signed in to change notification settings - Fork 4.6k
[WIP] Style engine: register styles for enqueuing and rendering #41424
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
52e5549
8c141e2
a55525a
cd3d2e9
0c9c2cf
42f4436
c72366e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,194 @@ | ||
| <?php | ||
| /** | ||
| * WP_Style_Engine_Renderer | ||
| * | ||
| * A library of CSS rule generators and render functions. | ||
| * | ||
| * @TODO Consider splitting out the rule generators (?). | ||
| * Or creating a base interface and then a bunch of separate render classes for each "layer", e.g., | ||
| * one for block supports, one for global styles. | ||
| * | ||
| * @package Gutenberg | ||
| */ | ||
|
|
||
| if ( class_exists( 'WP_Style_Engine_Renderer' ) ) { | ||
| return; | ||
| } | ||
|
|
||
| /** | ||
| * Renders CSS on the frontend. | ||
| * | ||
| * @access private | ||
| */ | ||
| class WP_Style_Engine_Renderer { | ||
| /** | ||
| * Prints registered styles in the page head or footer. | ||
| * | ||
| * @TODO this shares code with the styles engine class in generate(). Centralize. | ||
| * | ||
| * @see $this->enqueue_block_support_styles | ||
| */ | ||
| public static function render_registered_block_supports_styles() { | ||
| $style_engine = WP_Style_Engine::get_instance(); | ||
| $block_support_styles = $style_engine->get_registered_styles(); | ||
|
|
||
| if ( empty( $block_support_styles ) ) { | ||
| return; | ||
| } | ||
|
|
||
| $output = ''; | ||
|
|
||
| foreach ( $block_support_styles as $selector => $css_definitions ) { | ||
| $output .= self::generate_css_rule( $selector, $css_definitions, array( 'prettify' => true ) ); | ||
| } | ||
|
|
||
| echo "<style>\n$output</style>\n"; | ||
| } | ||
|
|
||
| /** | ||
| * Filters incoming CSS properties against WordPress Core's allowed CSS attributes in wp-includes/kses.php. | ||
| * | ||
| * @param string $property_declaration A CSS property declaration, e.g., `color: 'pink'`. | ||
| * | ||
| * @return string A filtered CSS property. Empty if not allowed. | ||
| */ | ||
| public static function sanitize_property_declaration( $property_declaration ) { | ||
| return esc_html( safecss_filter_attr( $property_declaration ) ); | ||
| } | ||
|
|
||
| /** | ||
| * Creates a string consisting of CSS property declarations suitable for the value of an HTML element's style attribute. | ||
| * | ||
| * @param array $css_definitions An collection of CSS definitions `[ [ 'color' => 'red' ] ]`. | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. References to |
||
| * | ||
| * @return string A concatenated string of CSS properties, e.g. `'color: red; font-size:12px'` | ||
| */ | ||
| public static function generate_inline_property_declarations( $css_definitions ) { | ||
| $css_rule_inline = ''; | ||
|
|
||
| if ( empty( $css_definitions ) ) { | ||
| return $css_rule_inline; | ||
| } | ||
| foreach ( $css_definitions as $definition => $value ) { | ||
| $filtered_css = self::sanitize_property_declaration( "{$definition}: {$value}" ); | ||
| if ( ! empty( $filtered_css ) ) { | ||
| $css_rule_inline .= $filtered_css . ';'; | ||
| } | ||
| } | ||
| return $css_rule_inline; | ||
| } | ||
|
|
||
| /** | ||
| * Creates a string consisting of a CSS rule. | ||
| * | ||
| * @param string $selector A CSS selector, e.g., `.some-class-name`. | ||
| * @param array $css_definitions An collection of CSS definitions `[ [ 'color' => 'red' ] ]`. | ||
| * @param array $options array( | ||
| * 'prettify' => (boolean) Whether to add carriage returns and indenting. | ||
| * 'indent' => (number) The number of tab indents to apply to the rule. Applies if `prettify` is `true`. | ||
| * );. | ||
| * | ||
| * @return string A CSS rule, e.g. `'.some-selector { color: red; font-size:12px }'` | ||
| */ | ||
| public static function generate_css_rule( $selector, $css_definitions, $options = array() ) { | ||
| $css_rule_block = ''; | ||
|
|
||
| if ( ! $selector || empty( $css_definitions ) ) { | ||
| return $css_rule_block; | ||
| } | ||
|
|
||
| $defaults = array( | ||
| 'prettify' => false, | ||
| 'indent' => 0, | ||
| ); | ||
| $options = wp_parse_args( $options, $defaults ); | ||
| $indent = str_repeat( "\t", $options['indent'] ); | ||
| $css_rule_block = $options['prettify'] ? "$indent$selector {\n" : "$selector { "; | ||
|
|
||
| foreach ( $css_definitions as $definition => $value ) { | ||
| $filtered_css = self::sanitize_property_declaration( "{$definition}: {$value}" ); | ||
| if ( ! empty( $filtered_css ) ) { | ||
| if ( $options['prettify'] ) { | ||
| $css_rule_block .= "\t$indent$filtered_css;\n"; | ||
| } else { | ||
| $css_rule_block .= $filtered_css . ';'; | ||
| } | ||
| } | ||
| } | ||
| $css_rule_block .= $options['prettify'] ? "$indent}\n" : ' }'; | ||
| return $css_rule_block; | ||
| } | ||
|
|
||
| // @TODO Using cascade layers should be opt-in. | ||
| /** | ||
| * Builds layers and styles rules from registered layers and styles for output. | ||
| */ | ||
| public static function enqueue_cascade_layers() { | ||
|
||
| $style_engine = WP_Style_Engine::get_instance(); | ||
| $registered_layers = $style_engine->get_registered_styles(); | ||
|
|
||
| if ( empty( $registered_layers ) ) { | ||
| return; | ||
| } | ||
|
|
||
| $layer_output = array(); | ||
| $styles_output = ''; | ||
|
|
||
| foreach ( $style_engine::STYLE_LAYERS as $layer_name ) { | ||
| if ( ! isset( $registered_layers[ $layer_name ] ) || empty( $registered_layers[ $layer_name ] ) ) { | ||
| continue; | ||
| } | ||
|
|
||
| $layer_output[] = $layer_name; | ||
| $styles_output .= "@layer {$layer_name} {\n"; | ||
|
|
||
| foreach ( $registered_layers[ $layer_name ] as $selector => $css_definitions ) { | ||
| $styles_output .= self::generate_css_rule( | ||
| $selector, | ||
| $css_definitions, | ||
| array( | ||
| 'prettify' => true, | ||
| 'indent' => 1, | ||
| ) | ||
| ); | ||
| } | ||
| $styles_output .= '}'; | ||
| } | ||
|
|
||
| if ( ! empty( $styles_output ) ) { | ||
| $layer_output = '@layer ' . implode( ', ', $layer_output ) . ";\n"; | ||
| wp_register_style( 'wp-styles-layers', false, array(), true, true ); | ||
| wp_add_inline_style( 'wp-styles-layers', $layer_output . $styles_output ); | ||
| wp_enqueue_style( 'wp-styles-layers' ); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Taken from gutenberg_enqueue_block_support_styles() | ||
| * | ||
| * This function takes care of adding inline styles | ||
| * in the proper place, depending on the theme in use. | ||
| * | ||
| * For block themes, it's loaded in the head. | ||
| * For classic ones, it's loaded in the body | ||
| * because the wp_head action happens before | ||
| * the render_block. | ||
| * | ||
| * @see gutenberg_enqueue_block_support_styles() | ||
| * | ||
| * @param int $priority To set the priority for the add_action. | ||
| */ | ||
| public static function enqueue_registered_styles( $priority = 10 ) { | ||
| $action_hook_name = 'wp_footer'; | ||
| if ( wp_is_block_theme() ) { | ||
| $action_hook_name = 'wp_head'; | ||
| } | ||
| add_action( 'wp_enqueue_scripts', array( __CLASS__, 'enqueue_cascade_layers' ) ); | ||
| add_action( | ||
| $action_hook_name, | ||
| array( __CLASS__, 'enqueue_cascade_layers' ), | ||
| $priority | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,80 @@ | ||
| <?php | ||
| /** | ||
| * WP_Style_Engine_Store | ||
| * | ||
| * Registers and stores styles to be processed or rendered on the frontend. | ||
| * | ||
| * @package Gutenberg | ||
| */ | ||
|
|
||
| if ( class_exists( 'WP_Style_Engine_Store' ) ) { | ||
| return; | ||
| } | ||
|
|
||
| /** | ||
| * Registers and stores styles to be processed or rendered on the frontend. | ||
| * | ||
| * For each style category we could have a separate object, e.g., | ||
| * $global_style_store = new WP_Style_Engine_Store(); | ||
| * $block_supports_style_store = new WP_Style_Engine_Store(); | ||
| * | ||
| * @access private | ||
| */ | ||
| class WP_Style_Engine_Store { | ||
| /** | ||
| * Registered styles. | ||
| * | ||
| * @var WP_Style_Engine_Store|null | ||
| */ | ||
| private $registered_styles = array(); | ||
|
|
||
| /** | ||
| * Gather internals. | ||
| */ | ||
| public function __construct( $layers = array() ) { | ||
| foreach ( $layers as $layer ) { | ||
| $this->registered_styles[ $layer ] = array(); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Register a style | ||
| * | ||
| * @param string $layer Unique key for a layer. | ||
| * @param string $key Unique key for a $style_data object. | ||
| * @param array $style_data Associative array of style information. | ||
| * @return boolean Whether registration was successful. | ||
| */ | ||
| public function register( $layer, $key, $style_data ) { | ||
| if ( empty( $layer ) || empty( $key ) || empty( $style_data ) ) { | ||
| return false; | ||
| } | ||
|
|
||
| if ( isset( $this->registered_styles[ $layer ][ $key ] ) ) { | ||
| $style_data = array_unique( array_merge( $this->registered_styles[ $layer ][ $key ], $style_data ) ); | ||
| } | ||
| $this->registered_styles[ $layer ][ $key ] = $style_data; | ||
| return true; | ||
| } | ||
|
|
||
| /** | ||
| * Retrieves style data from the store. If neither $layer nor $key are provided, | ||
| * this method will return everything in the store. | ||
| * | ||
| * @param string $layer Optional unique key for a layer to return all styles for a layer. | ||
| * @param string $key Optional unique key for a $style_data object to return a single style object. | ||
| * | ||
| * @return array Registered styles | ||
| */ | ||
| public function get( $layer = null, $key = null ) { | ||
| if ( isset( $this->registered_styles[ $layer ][ $key ] ) ) { | ||
| return $this->registered_styles[ $layer ][ $key ]; | ||
| } | ||
|
|
||
| if ( isset( $this->registered_styles[ $layer ] ) ) { | ||
| return $this->registered_styles[ $layer ]; | ||
| } | ||
|
|
||
| return $this->registered_styles; | ||
| } | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.