@wordpress/style-engine 0.13.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/README.md +162 -4
  3. package/class-wp-style-engine-css-declarations.php +27 -3
  4. package/class-wp-style-engine-css-rule.php +17 -3
  5. package/class-wp-style-engine-css-rules-store.php +49 -0
  6. package/class-wp-style-engine-processor.php +68 -26
  7. package/class-wp-style-engine.php +236 -107
  8. package/package.json +2 -2
  9. package/phpunit/class-wp-style-engine-css-declarations-test.php +85 -7
  10. package/phpunit/class-wp-style-engine-css-rule-test.php +38 -7
  11. package/phpunit/class-wp-style-engine-css-rules-store-test.php +44 -2
  12. package/phpunit/class-wp-style-engine-processor-test.php +170 -31
  13. package/phpunit/class-wp-style-engine-test.php +219 -22
  14. package/build-types/index.d.ts +0 -23
  15. package/build-types/index.d.ts.map +0 -1
  16. package/build-types/styles/border/index.d.ts +0 -10
  17. package/build-types/styles/border/index.d.ts.map +0 -1
  18. package/build-types/styles/color/background.d.ts +0 -14
  19. package/build-types/styles/color/background.d.ts.map +0 -1
  20. package/build-types/styles/color/gradient.d.ts +0 -14
  21. package/build-types/styles/color/gradient.d.ts.map +0 -1
  22. package/build-types/styles/color/index.d.ts +0 -10
  23. package/build-types/styles/color/index.d.ts.map +0 -1
  24. package/build-types/styles/color/text.d.ts +0 -14
  25. package/build-types/styles/color/text.d.ts.map +0 -1
  26. package/build-types/styles/constants.d.ts +0 -4
  27. package/build-types/styles/constants.d.ts.map +0 -1
  28. package/build-types/styles/index.d.ts +0 -5
  29. package/build-types/styles/index.d.ts.map +0 -1
  30. package/build-types/styles/spacing/index.d.ts +0 -6
  31. package/build-types/styles/spacing/index.d.ts.map +0 -1
  32. package/build-types/styles/spacing/margin.d.ts +0 -10
  33. package/build-types/styles/spacing/margin.d.ts.map +0 -1
  34. package/build-types/styles/spacing/padding.d.ts +0 -10
  35. package/build-types/styles/spacing/padding.d.ts.map +0 -1
  36. package/build-types/styles/typography/index.d.ts +0 -14
  37. package/build-types/styles/typography/index.d.ts.map +0 -1
  38. package/build-types/styles/utils.d.ts +0 -48
  39. package/build-types/styles/utils.d.ts.map +0 -1
  40. package/build-types/types.d.ts +0 -86
  41. package/build-types/types.d.ts.map +0 -1
  42. package/tsconfig.tsbuildinfo +0 -1
package/CHANGELOG.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.14.0 (2022-08-10)
6
+
5
7
  ## 0.13.0 (2022-07-27)
6
8
 
7
9
  ## 0.12.0 (2022-07-13)
package/README.md CHANGED
@@ -2,6 +2,158 @@
2
2
 
3
3
  The Style Engine powering global styles and block customizations.
4
4
 
5
+ ## Backend API
6
+
7
+ ### wp_style_engine_get_styles()
8
+
9
+ Global public function to generate styles from a single style object, e.g., the value of
10
+ a [block's attributes.style object](https://developer.wordpress.org/block-editor/reference-guides/theme-json-reference/theme-json-living/#styles)
11
+ or
12
+ the [top level styles in theme.json](https://developer.wordpress.org/block-editor/reference-guides/block-api/block-supports/)
13
+ .
14
+
15
+ _Parameters_
16
+
17
+ - _$block_styles_ `array` A block's `attributes.style` object or the top level styles in theme.json
18
+ - _$options_ `array<string|boolean>` An array of options to determine the output.
19
+ - _context_ `string` An identifier describing the origin of the style object, e.g., 'block-supports' or '
20
+ global-styles'. Default is 'block-supports'.
21
+ - _enqueue_ `boolean` When `true` will attempt to store and enqueue for rendering in a `style` tag on the site frontend.
22
+ - _convert_vars_to_classnames_ `boolean` Whether to skip converting CSS var:? values to var( --wp--preset--\* )
23
+ values. Default is `false`.
24
+ - _selector_ `string` When a selector is passed, `generate()` will return a full CSS rule `$selector { ...rules }`,
25
+ otherwise a concatenated string of properties and values.
26
+
27
+ _Returns_
28
+ `array<string|array>|null`
29
+
30
+ ```php
31
+ array(
32
+ 'css' => (string) A CSS ruleset or declarations block formatted to be placed in an HTML `style` attribute or tag.
33
+ 'declarations' => (array) An array of property/value pairs representing parsed CSS declarations.
34
+ 'classnames' => (string) Classnames separated by a space.
35
+ );
36
+ ```
37
+
38
+ It will return compiled CSS declarations for inline styles, or, where a selector is provided, a complete CSS rule.
39
+
40
+ To enqueue a style for rendering in the site's frontend, the `$options` array requires the following:
41
+
42
+ 1. **selector (string)** - this is the CSS selector for your block style CSS declarations.
43
+ 2. **context (string)** - this tells the style engine where to store the styles. Styles in the same context will be
44
+ batched together and printed in the same HTML style tag. The default is `'block-supports'`.
45
+ 3. **enqueue (boolean)** - tells the style engine to store the styles.
46
+
47
+ `wp_style_engine_get_styles` will return the compiled CSS and CSS declarations array.
48
+
49
+ #### Usage
50
+
51
+ ```php
52
+ $block_styles = array(
53
+ 'spacing' => array( 'padding' => '100px' )
54
+ );
55
+ $styles = wp_style_engine_get_styles(
56
+ $block_styles,
57
+ array(
58
+ 'selector' => '.a-selector',
59
+ 'context' => 'block-supports',
60
+ 'enqueue' => true,
61
+ )
62
+ );
63
+ print_r( $styles );
64
+
65
+ /*
66
+ array(
67
+ 'css' => '.a-selector{padding:10px}'
68
+ 'declarations' => array( 'padding' => '100px' )
69
+ )
70
+ */
71
+ ```
72
+
73
+ ### wp_style_engine_get_stylesheet_from_css_rules()
74
+
75
+ Use this function to compile and return a stylesheet for any CSS rules. The style engine will automatically merge declarations and combine selectors.
76
+
77
+ This function acts as a CSS compiler, but will also enqueue styles for rendering where `enqueue` and `context` strings are passed in the options.
78
+
79
+ _Parameters_
80
+
81
+ - _$css_rules_ `array<array>`
82
+ - _$options_ `array<string|boolean>` An array of options to determine the output.
83
+ - _context_ `string` An identifier describing the origin of the style object, e.g., 'block-supports' or '
84
+ global-styles'. Default is 'block-supports'.
85
+ - _enqueue_ `boolean` When `true` will store using the `context` value as a key.
86
+
87
+ _Returns_
88
+ `string` A compiled CSS string based on `$css_rules`.
89
+
90
+ #### Usage
91
+
92
+ ```php
93
+ $styles = array(
94
+ array(
95
+ 'selector'. => '.wp-pumpkin',
96
+ 'declarations' => array( 'color' => 'orange' )
97
+ ),
98
+ array(
99
+ 'selector'. => '.wp-tomato',
100
+ 'declarations' => array( 'color' => 'red' )
101
+ ),
102
+ array(
103
+ 'selector'. => '.wp-tomato',
104
+ 'declarations' => array( 'padding' => '100px' )
105
+ ),
106
+ array(
107
+ 'selector'. => '.wp-kumquat',
108
+ 'declarations' => array( 'color' => 'orange' )
109
+ ),
110
+ );
111
+
112
+ $stylesheet = wp_style_engine_get_stylesheet_from_css_rules(
113
+ $styles,
114
+ array(
115
+ 'context' => 'block-supports', // Indicates that these styles should be stored with block supports CSS.
116
+ 'enqueue' => true, // Render the styles for output.
117
+ )
118
+ );
119
+ print_r( $stylesheet ); // .wp-pumpkin, .wp-kumquat {color:orange}.wp-tomato{color:red;padding:100px}
120
+ ```
121
+
122
+ ### wp_style_engine_get_stylesheet_from_store()
123
+
124
+ Returns compiled CSS from a store, if found.
125
+
126
+ _Parameters_
127
+
128
+ - _$store_key_ `string` An identifier describing the origin of the style object, e.g., 'block-supports' or ' global-styles'. Default is 'block-supports'.
129
+
130
+ _Returns_
131
+ `string` A compiled CSS string from the stored CSS rules.
132
+
133
+ #### Usage
134
+
135
+ ```php
136
+ // First register some styles.
137
+ $styles = array(
138
+ array(
139
+ 'selector'. => '.wp-apple',
140
+ 'declarations' => array( 'color' => 'green' )
141
+ ),
142
+ );
143
+
144
+ $stylesheet = wp_style_engine_get_stylesheet_from_css_rules(
145
+ $styles,
146
+ array(
147
+ 'context' => 'fruit-styles',
148
+ 'enqueue' => true,
149
+ )
150
+ );
151
+
152
+ // Later, fetch compiled rules from store.
153
+ $stylesheet = gutenberg_style_engine_get_stylesheet_from_store( 'fruit-styles' );
154
+ print_r( $stylesheet ); // .wp-apple{color:green;}
155
+ ```
156
+
5
157
  ## Installation (JS only)
6
158
 
7
159
  Install the module
@@ -10,13 +162,18 @@ Install the module
10
162
  npm install @wordpress/style-engine --save
11
163
  ```
12
164
 
13
- _This package assumes that your code will run in an **ES2015+** environment. If you're using an environment that has limited or no support for such language features and APIs, you should include [the polyfill shipped in `@wordpress/babel-preset-default`](https://github.com/WordPress/gutenberg/tree/HEAD/packages/babel-preset-default#polyfill) in your code._
165
+ _This package assumes that your code will run in an **ES2015+** environment. If you're using an environment that has
166
+ limited or no support for such language features and APIs, you should
167
+ include [the polyfill shipped in `@wordpress/babel-preset-default`](https://github.com/WordPress/gutenberg/tree/HEAD/packages/babel-preset-default#polyfill)
168
+ in your code._
14
169
 
15
170
  ## Important
16
171
 
17
- This Package is considered experimental at the moment. The idea is to have a package used to generate styles based on a style object that is consistent between: backend, frontend, block style object and theme.json.
172
+ This Package is considered experimental at the moment. The idea is to have a package used to generate styles based on a
173
+ style object that is consistent between: backend, frontend, block style object and theme.json.
18
174
 
19
- Because this package is experimental and still in development it does not yet generate a `wp.styleEngine` global. To get there, the following tasks need to be completed:
175
+ Because this package is experimental and still in development it does not yet generate a `wp.styleEngine` global. To get
176
+ there, the following tasks need to be completed:
20
177
 
21
178
  **TODO List:**
22
179
 
@@ -26,7 +183,8 @@ Because this package is experimental and still in development it does not yet ge
26
183
  - Support generating styles in the backend (block supports and theme.json stylesheet). (Ongoing)
27
184
  - Refactor all block styles to use the style engine server side. (Ongoing)
28
185
  - Consolidate global and block style rendering and enqueuing
29
- - Refactor all blocks to consistently use the "style" attribute for all customizations (get rid of the preset specific attributes).
186
+ - Refactor all blocks to consistently use the "style" attribute for all customizations (get rid of the preset specific
187
+ attributes).
30
188
 
31
189
  See [Tracking: Add a Style Engine to manage rendering block styles #38167](https://github.com/WordPress/gutenberg/issues/38167)
32
190
 
@@ -113,18 +113,42 @@ class WP_Style_Engine_CSS_Declarations {
113
113
  return $this->declarations;
114
114
  }
115
115
 
116
+ /**
117
+ * Filters a CSS property + value pair.
118
+ *
119
+ * @param string $property The CSS property.
120
+ * @param string $value The value to be filtered.
121
+ * @param string $spacer The spacer between the colon and the value. Defaults to an empty string.
122
+ *
123
+ * @return string The filtered declaration as a single string.
124
+ */
125
+ protected static function filter_declaration( $property, $value, $spacer = '' ) {
126
+ if ( isset( $property ) && isset( $value ) ) {
127
+ return safecss_filter_attr( "{$property}:{$spacer}{$value}" );
128
+ }
129
+ return '';
130
+ }
131
+
116
132
  /**
117
133
  * Filters and compiles the CSS declarations.
118
134
  *
135
+ * @param boolean $should_prettify Whether to add spacing, new lines and indents.
136
+ * @param number $indent_count The number of tab indents to apply to the rule. Applies if `prettify` is `true`.
137
+ *
119
138
  * @return string The CSS declarations.
120
139
  */
121
- public function get_declarations_string() {
140
+ public function get_declarations_string( $should_prettify = false, $indent_count = 0 ) {
122
141
  $declarations_array = $this->get_declarations();
123
142
  $declarations_output = '';
143
+ $indent = $should_prettify ? str_repeat( "\t", $indent_count ) : '';
144
+ $suffix = $should_prettify ? ' ' : '';
145
+ $suffix = $should_prettify && $indent_count > 0 ? "\n" : $suffix;
146
+
124
147
  foreach ( $declarations_array as $property => $value ) {
125
- $filtered_declaration = esc_html( safecss_filter_attr( "{$property}: {$value}" ) );
148
+ $spacer = $should_prettify ? ' ' : '';
149
+ $filtered_declaration = static::filter_declaration( $property, $value, $spacer );
126
150
  if ( $filtered_declaration ) {
127
- $declarations_output .= $filtered_declaration . '; ';
151
+ $declarations_output .= "{$indent}{$filtered_declaration};$suffix";
128
152
  }
129
153
  }
130
154
  return rtrim( $declarations_output );
@@ -51,7 +51,7 @@ class WP_Style_Engine_CSS_Rule {
51
51
  *
52
52
  * @param string $selector The CSS selector.
53
53
  *
54
- * @return WP_Style_Engine_CSS_Rule Returns the object to allow chaining of methods.
54
+ * @return WP_Style_Engine_CSS_Rule|void Returns the object to allow chaining of methods.
55
55
  */
56
56
  public function set_selector( $selector ) {
57
57
  if ( empty( $selector ) ) {
@@ -107,9 +107,23 @@ class WP_Style_Engine_CSS_Rule {
107
107
  /**
108
108
  * Get the CSS.
109
109
  *
110
+ * @param boolean $should_prettify Whether to add spacing, new lines and indents.
111
+ * @param number $indent_count The number of tab indents to apply to the rule. Applies if `prettify` is `true`.
112
+ *
110
113
  * @return string
111
114
  */
112
- public function get_css() {
113
- return $this->get_selector() . ' {' . $this->declarations->get_declarations_string() . '}';
115
+ public function get_css( $should_prettify = false, $indent_count = 0 ) {
116
+ $rule_indent = $should_prettify ? str_repeat( "\t", $indent_count ) : '';
117
+ $declarations_indent = $should_prettify ? $indent_count + 1 : 0;
118
+ $new_line = $should_prettify ? "\n" : '';
119
+ $space = $should_prettify ? ' ' : '';
120
+ $selector = $should_prettify ? str_replace( ',', ",\n", $this->get_selector() ) : $this->get_selector();
121
+ $css_declarations = $this->declarations->get_declarations_string( $should_prettify, $declarations_indent );
122
+
123
+ if ( empty( $css_declarations ) ) {
124
+ return '';
125
+ }
126
+
127
+ return "{$rule_indent}{$selector}{$space}{{$new_line}{$css_declarations}{$new_line}{$rule_indent}}";
114
128
  }
115
129
  }
@@ -27,6 +27,14 @@ class WP_Style_Engine_CSS_Rules_Store {
27
27
  */
28
28
  protected static $stores = array();
29
29
 
30
+
31
+ /**
32
+ * The store name.
33
+ *
34
+ * @var string
35
+ */
36
+ protected $name = '';
37
+
30
38
  /**
31
39
  * An array of CSS Rules objects assigned to the store.
32
40
  *
@@ -44,9 +52,50 @@ class WP_Style_Engine_CSS_Rules_Store {
44
52
  public static function get_store( $store_name = 'default' ) {
45
53
  if ( ! isset( static::$stores[ $store_name ] ) ) {
46
54
  static::$stores[ $store_name ] = new static();
55
+ // Set the store name.
56
+ static::$stores[ $store_name ]->set_name( $store_name );
47
57
  }
48
58
  return static::$stores[ $store_name ];
49
59
  }
60
+
61
+ /**
62
+ * Get an array of all available stores.
63
+ *
64
+ * @return WP_Style_Engine_CSS_Rules_Store[]
65
+ */
66
+ public static function get_stores() {
67
+ return static::$stores;
68
+ }
69
+
70
+ /**
71
+ * Clears all stores from static::$stores.
72
+ *
73
+ * @return void
74
+ */
75
+ public static function remove_all_stores() {
76
+ static::$stores = array();
77
+ }
78
+
79
+ /**
80
+ * Set the store name.
81
+ *
82
+ * @param string $name The store name.
83
+ *
84
+ * @return void
85
+ */
86
+ public function set_name( $name ) {
87
+ $this->name = $name;
88
+ }
89
+
90
+ /**
91
+ * Get the store name.
92
+ *
93
+ * @return string
94
+ */
95
+ public function get_name() {
96
+ return $this->name;
97
+ }
98
+
50
99
  /**
51
100
  * Get an array of all rules.
52
101
  *
@@ -2,7 +2,7 @@
2
2
  /**
3
3
  * WP_Style_Engine_Processor
4
4
  *
5
- * Compiles styles from a store of CSS rules.
5
+ * Compiles styles from stores or collection of CSS rules.
6
6
  *
7
7
  * @package Gutenberg
8
8
  */
@@ -12,42 +12,86 @@ if ( class_exists( 'WP_Style_Engine_Processor' ) ) {
12
12
  }
13
13
 
14
14
  /**
15
- * Compiles styles from a store of CSS rules.
15
+ * Compiles styles from stores or collection of CSS rules.
16
16
  *
17
17
  * @access private
18
18
  */
19
19
  class WP_Style_Engine_Processor {
20
20
 
21
21
  /**
22
- * The Style-Engine Store object.
22
+ * The Style-Engine Store objects
23
23
  *
24
- * @var WP_Style_Engine_CSS_Rules_Store
24
+ * @var WP_Style_Engine_CSS_Rules_Store[]
25
25
  */
26
- protected $store;
26
+ protected $stores = array();
27
27
 
28
28
  /**
29
- * Constructor.
29
+ * The set of CSS rules that this processor will work on.
30
30
  *
31
- * @param WP_Style_Engine_CSS_Rules_Store $store The store to render.
31
+ * @var WP_Style_Engine_CSS_Rule[]
32
32
  */
33
- public function __construct( WP_Style_Engine_CSS_Rules_Store $store ) {
34
- $this->store = $store;
33
+ protected $css_rules = array();
34
+
35
+ /**
36
+ * Add a store to the processor.
37
+ *
38
+ * @param WP_Style_Engine_CSS_Rules_Store $store The store to add.
39
+ */
40
+ public function add_store( WP_Style_Engine_CSS_Rules_Store $store ) {
41
+ $this->stores[ $store->get_name() ] = $store;
42
+ }
43
+
44
+ /**
45
+ * Adds rules to be processed.
46
+ *
47
+ * @param WP_Style_Engine_CSS_Rule|WP_Style_Engine_CSS_Rule[] $css_rules A single, or an array of, WP_Style_Engine_CSS_Rule objects from a store or otherwise.
48
+ */
49
+ public function add_rules( $css_rules ) {
50
+ if ( ! is_array( $css_rules ) ) {
51
+ $css_rules = array( $css_rules );
52
+ }
53
+ foreach ( $css_rules as $rule ) {
54
+ $selector = $rule->get_selector();
55
+ if ( isset( $this->css_rules[ $selector ] ) ) {
56
+ $this->css_rules[ $selector ]->add_declarations( $rule->get_declarations() );
57
+ continue;
58
+ }
59
+ $this->css_rules[ $rule->get_selector() ] = $rule;
60
+ }
35
61
  }
36
62
 
37
63
  /**
38
64
  * Get the CSS rules as a string.
39
65
  *
66
+ * @param array $options array(
67
+ * 'optimize' => (boolean) Whether to optimize the CSS output, e.g., combine rules.
68
+ * 'prettify' => (boolean) Whether to add new lines to output.
69
+ * );.
70
+ *
40
71
  * @return string The computed CSS.
41
72
  */
42
- public function get_css() {
73
+ public function get_css( $options = array() ) {
74
+ $defaults = array(
75
+ 'optimize' => true,
76
+ 'prettify' => false,
77
+ );
78
+ $options = wp_parse_args( $options, $defaults );
79
+
80
+ // If we have stores, get the rules from them.
81
+ foreach ( $this->stores as $store ) {
82
+ $this->add_rules( $store->get_all_rules() );
83
+ }
84
+
43
85
  // Combine CSS selectors that have identical declarations.
44
- $this->combine_rules_selectors();
86
+ if ( true === $options['optimize'] ) {
87
+ $this->combine_rules_selectors();
88
+ }
45
89
 
46
90
  // Build the CSS.
47
- $css = '';
48
- $rules = $this->store->get_all_rules();
49
- foreach ( $rules as $rule ) {
50
- $css .= $rule->get_css();
91
+ $css = '';
92
+ foreach ( $this->css_rules as $rule ) {
93
+ $css .= $rule->get_css( $options['prettify'] );
94
+ $css .= $options['prettify'] ? "\n" : '';
51
95
  }
52
96
  return $css;
53
97
  }
@@ -58,14 +102,12 @@ class WP_Style_Engine_Processor {
58
102
  * @return void
59
103
  */
60
104
  private function combine_rules_selectors() {
61
- $rules = $this->store->get_all_rules();
62
-
63
105
  // Build an array of selectors along with the JSON-ified styles to make comparisons easier.
64
106
  $selectors_json = array();
65
- foreach ( $rules as $selector => $rule ) {
107
+ foreach ( $this->css_rules as $rule ) {
66
108
  $declarations = $rule->get_declarations()->get_declarations();
67
109
  ksort( $declarations );
68
- $selectors_json[ $selector ] = json_encode( $declarations );
110
+ $selectors_json[ $rule->get_selector() ] = wp_json_encode( $declarations );
69
111
  }
70
112
 
71
113
  // Combine selectors that have the same styles.
@@ -76,18 +118,18 @@ class WP_Style_Engine_Processor {
76
118
  if ( 1 >= count( $duplicates ) ) {
77
119
  continue;
78
120
  }
121
+
122
+ $declarations = $this->css_rules[ $selector ]->get_declarations();
123
+
79
124
  foreach ( $duplicates as $key ) {
80
125
  // Unset the duplicates from the $selectors_json array to avoid looping through them as well.
81
126
  unset( $selectors_json[ $key ] );
82
- // Remove the rules from the store.
83
- $this->store->remove_rule( $key );
127
+ // Remove the rules from the rules collection.
128
+ unset( $this->css_rules[ $key ] );
84
129
  }
85
130
  // Create a new rule with the combined selectors.
86
- $new_rule = $this->store->add_rule( implode( ',', $duplicates ) );
87
- // Set the declarations. The extra check is in place because `add_rule` in the store can return `null`.
88
- if ( $new_rule ) {
89
- $new_rule->add_declarations( $rules[ $selector ]->get_declarations() );
90
- }
131
+ $duplicate_selectors = implode( ',', $duplicates );
132
+ $this->css_rules[ $duplicate_selectors ] = new WP_Style_Engine_CSS_Rule( $duplicate_selectors, $declarations );
91
133
  }
92
134
  }
93
135
  }