From 43fa6cf8dbfb9027ab20d3da639da5e5e08fa4a1 Mon Sep 17 00:00:00 2001
From: Andrew Serong <14988353+andrewserong@users.noreply.github.com>
Date: Fri, 11 Mar 2022 17:23:30 +1100
Subject: [PATCH 1/6] Try: Alternative approach to rendering common layout
styles from presets
---
lib/block-supports/layout.php | 102 +++++++++++
lib/class-wp-style-engine-gutenberg.php | 115 ++++++++++++
.../wordpress-5.9/class-wp-theme-json-5-9.php | 17 ++
lib/compat/wordpress-5.9/theme.json | 12 ++
.../class-wp-theme-json-gutenberg.php | 169 ++++++++++++++++++
lib/load.php | 5 +
6 files changed, 420 insertions(+)
create mode 100644 lib/class-wp-style-engine-gutenberg.php
diff --git a/lib/block-supports/layout.php b/lib/block-supports/layout.php
index 3b4f2b5a9bd4b..305e41858b67c 100644
--- a/lib/block-supports/layout.php
+++ b/lib/block-supports/layout.php
@@ -25,6 +25,108 @@ function gutenberg_register_layout_support( $block_type ) {
}
}
+function gutenberg_generate_common_flow_layout_styles() {
+ $style_engine = WP_Style_Engine_Gutenberg::get_instance();
+
+ $style_engine->add_style(
+ 'wp-layout-flow',
+ array(
+ 'selector' => '.alignleft',
+ 'rules' => array(
+ 'float' => 'left',
+ 'margin-right' => '2em',
+ 'margin-left' => '0',
+ ),
+ )
+ );
+
+ $style_engine->add_style(
+ 'wp-layout-flow',
+ array(
+ 'selector' => '.alignright',
+ 'rules' => array(
+ 'float' => 'right',
+ 'margin-left' => '2em',
+ 'margin-right' => '0',
+ ),
+ )
+ );
+
+ $style_engine->add_style(
+ 'wp-layout-flow',
+ array(
+ 'selector' => '> .alignfull',
+ 'rules' => array(
+ 'max-width' => 'none',
+ ),
+ )
+ );
+
+ $style_engine->add_style(
+ 'wp-layout-flow--global-gap',
+ array(
+ 'selector' => '> *',
+ 'rules' => array(
+ 'margin-top' => '0',
+ 'margin-bottom' => '0',
+ ),
+ )
+ );
+
+ $style_engine->add_style(
+ 'wp-layout-flow--global-gap',
+ array(
+ 'selector' => '> * + *',
+ 'rules' => array(
+ 'margin-top' => 'var( --wp--style--block-gap )',
+ 'margin-bottom' => '0',
+ ),
+ )
+ );
+}
+
+function gutenberg_generate_common_flex_layout_styles() {
+ $style_engine = WP_Style_Engine_Gutenberg::get_instance();
+
+ $style_engine->add_style(
+ 'wp-layout-flex',
+ array(
+ 'rules' => array(
+ 'display' => 'flex',
+ 'gap' => '0.5em',
+ ),
+ )
+ );
+
+ $style_engine->add_style(
+ 'wp-layout-flex',
+ array(
+ 'selector' => '> *',
+ 'rules' => array(
+ 'margin' => '0',
+ ),
+ )
+ );
+}
+
+function gutenberg_get_layout_preset_styles( $preset_metadata, $presets_by_origin ) {
+ WP_Style_Engine_Gutenberg::get_instance()->reset();
+
+ $presets = end( $presets_by_origin );
+
+ foreach ( $presets as $preset ) {
+ if ( isset( $preset['type'] ) ) {
+ if ( 'flow' === $preset['type'] ) {
+ $output_styles = gutenberg_generate_common_flow_layout_styles();
+ } else if ( 'flex' === $preset['type'] ) {
+ $output_styles = gutenberg_generate_common_flex_layout_styles();
+ }
+ }
+ }
+
+ return WP_Style_Engine_Gutenberg::get_instance()->get_generated_styles();
+}
+
/**
* Generates the CSS corresponding to the provided layout.
*
diff --git a/lib/class-wp-style-engine-gutenberg.php b/lib/class-wp-style-engine-gutenberg.php
new file mode 100644
index 0000000000000..998a7b54fe60d
--- /dev/null
+++ b/lib/class-wp-style-engine-gutenberg.php
@@ -0,0 +1,115 @@
+registered_styles = array();
+ }
+
+ public function get_generated_styles() {
+ $output = '';
+ foreach ( $this->registered_styles as $selector => $rules ) {
+ $output .= "{$selector} {\n";
+
+ if ( is_string( $rules ) ) {
+ $output .= ' ';
+ $output .= $rules;
+ } else {
+ foreach ( $rules as $rule => $value ) {
+ $output .= " {$rule}: {$value};\n";
+ }
+ }
+ $output .= "}\n";
+ }
+ return $output;
+ }
+
+ /**
+ * Stores style rules for a given CSS selector (the key) and returns an associated classname.
+ *
+ * @param string $key A class name used to construct a key.
+ * @param array $options An array of options, rules, and selector for constructing the rules.
+ *
+ * @return string The class name for the added style.
+ */
+ public function add_style( $key, $options ) {
+ $class = ! empty( $options['suffix'] ) ? $key . '-' . sanitize_title( $options['suffix'] ) : $key;
+ $selector = ! empty( $options['selector'] ) ? ' ' . trim( $options['selector'] ) : '';
+ $rules = ! empty( $options['rules'] ) ? $options['rules'] : array();
+ $prefix = ! empty( $options['prefix'] ) ? $options['prefix'] : '.';
+
+ if ( ! $class ) {
+ return;
+ }
+
+ $this->registered_styles[ $prefix . $class . $selector ] = $rules;
+
+ return $class;
+ }
+
+ /**
+ * Render registered styles as key { ...rules } for final output.
+ */
+ public function output_styles() {
+ $output = $this->get_generated_styles();
+ echo "\n";
+ }
+}
diff --git a/lib/compat/wordpress-5.9/class-wp-theme-json-5-9.php b/lib/compat/wordpress-5.9/class-wp-theme-json-5-9.php
index 5ad476b7d13bb..5ed973d570f05 100644
--- a/lib/compat/wordpress-5.9/class-wp-theme-json-5-9.php
+++ b/lib/compat/wordpress-5.9/class-wp-theme-json-5-9.php
@@ -948,6 +948,20 @@ protected static function compute_preset_classes( $settings, $selector, $origins
$stylesheet = '';
foreach ( static::PRESETS_METADATA as $preset_metadata ) {
+ if ( isset( $preset_metadata['type'] ) && 'layout' === $preset_metadata['type'] ) {
+
+ $preset_per_origin = _wp_array_get( $settings, $preset_metadata['path'], array() );
+
+ if ( ! empty( $preset_per_origin ) ) {
+ if ( isset( $preset_metadata['value_func'] ) &&
+ is_callable( $preset_metadata['value_func'] )
+ ) {
+ $value_func = $preset_metadata['value_func'];
+ $stylesheet .= call_user_func( $value_func, $preset_metadata, $preset_per_origin );
+ continue;
+ }
+ }
+ }
$slugs = static::get_settings_slugs( $settings, $preset_metadata, $origins );
foreach ( $preset_metadata['classes'] as $class => $property ) {
foreach ( $slugs as $slug ) {
@@ -1135,6 +1149,9 @@ protected static function replace_slug_in_string( $input, $slug ) {
protected static function compute_preset_vars( $settings, $origins ) {
$declarations = array();
foreach ( static::PRESETS_METADATA as $preset_metadata ) {
+ if ( isset( $preset_metadata['type'] ) && 'layout' === $preset_metadata['type'] ) {
+ continue;
+ }
$values_by_slug = static::get_settings_values_by_slug( $settings, $preset_metadata, $origins );
foreach ( $values_by_slug as $slug => $value ) {
$declarations[] = array(
diff --git a/lib/compat/wordpress-5.9/theme.json b/lib/compat/wordpress-5.9/theme.json
index 7691aa4a64e6a..b1c0b706a6088 100644
--- a/lib/compat/wordpress-5.9/theme.json
+++ b/lib/compat/wordpress-5.9/theme.json
@@ -185,6 +185,18 @@
],
"text": true
},
+ "layout": {
+ "types": [
+ {
+ "slug": "flow",
+ "type": "flow"
+ },
+ {
+ "slug": "flex",
+ "type": "flex"
+ }
+ ]
+ },
"spacing": {
"blockGap": null,
"margin": false,
diff --git a/lib/compat/wordpress-6.0/class-wp-theme-json-gutenberg.php b/lib/compat/wordpress-6.0/class-wp-theme-json-gutenberg.php
index 567dfb0cf4027..3a293e5433fc7 100644
--- a/lib/compat/wordpress-6.0/class-wp-theme-json-gutenberg.php
+++ b/lib/compat/wordpress-6.0/class-wp-theme-json-gutenberg.php
@@ -15,6 +15,175 @@
* @access private
*/
class WP_Theme_JSON_Gutenberg extends WP_Theme_JSON_5_9 {
+ /**
+ * Presets are a set of values that serve
+ * to bootstrap some styles: colors, font sizes, etc.
+ *
+ * They are a unkeyed array of values such as:
+ *
+ * ```php
+ * array(
+ * array(
+ * 'slug' => 'unique-name-within-the-set',
+ * 'name' => 'Name for the UI',
+ * => 'value'
+ * ),
+ * )
+ * ```
+ *
+ * This contains the necessary metadata to process them:
+ *
+ * - path => Where to find the preset within the settings section.
+ * - prevent_override => Whether a theme preset with the same slug as a default preset
+ * should not override it or the path to a setting for the same
+ * When defaults.
+ * The relationship between whether to override the defaults
+ * and whether the defaults are enabled is inverse:
+ * - If defaults are enabled => theme presets should not be overriden
+ * - If defaults are disabled => theme presets should be overriden
+ * For example, a theme sets defaultPalette to false,
+ * making the default palette hidden from the user.
+ * In that case, we want all the theme presets to be present,
+ * so they should override the defaults by setting this false.
+ * - value_key => the key that represents the value
+ * - value_func => optionally, instead of value_key, a function to generate
+ * the value that takes a preset as an argument
+ * (either value_key or value_func should be present)
+ * - css_vars => template string to use in generating the CSS Custom Property.
+ * Example output: "--wp--preset--duotone--blue: " will generate as many CSS Custom Properties as presets defined
+ * substituting the $slug for the slug's value for each preset value.
+ * - classes => array containing a structure with the classes to
+ * generate for the presets, where for each array item
+ * the key is the class name and the value the property name.
+ * The "$slug" substring will be replaced by the slug of each preset.
+ * For example:
+ * 'classes' => array(
+ * '.has-$slug-color' => 'color',
+ * '.has-$slug-background-color' => 'background-color',
+ * '.has-$slug-border-color' => 'border-color',
+ * )
+ * - properties => array of CSS properties to be used by kses to
+ * validate the content of each preset
+ * by means of the remove_insecure_properties method.
+ */
+ const PRESETS_METADATA = array(
+ array(
+ 'path' => array( 'color', 'palette' ),
+ 'prevent_override' => array( 'color', 'defaultPalette' ),
+ 'use_default_presets' => array( 'color', 'defaultPalette' ),
+ 'use_default_names' => false,
+ 'value_key' => 'color',
+ 'css_vars' => '--wp--preset--color--$slug',
+ 'classes' => array(
+ '.has-$slug-color' => 'color',
+ '.has-$slug-background-color' => 'background-color',
+ '.has-$slug-border-color' => 'border-color',
+ ),
+ 'properties' => array( 'color', 'background-color', 'border-color' ),
+ ),
+ array(
+ 'path' => array( 'color', 'gradients' ),
+ 'prevent_override' => array( 'color', 'defaultGradients' ),
+ 'use_default_presets' => array( 'color', 'defaultGradients' ),
+ 'use_default_names' => false,
+ 'value_key' => 'gradient',
+ 'css_vars' => '--wp--preset--gradient--$slug',
+ 'classes' => array( '.has-$slug-gradient-background' => 'background' ),
+ 'properties' => array( 'background' ),
+ ),
+ array(
+ 'path' => array( 'color', 'duotone' ),
+ 'prevent_override' => array( 'color', 'defaultDuotone' ),
+ 'use_default_presets' => array( 'color', 'defaultDuotone' ),
+ 'use_default_names' => false,
+ 'value_func' => 'gutenberg_get_duotone_filter_property',
+ 'css_vars' => '--wp--preset--duotone--$slug',
+ 'classes' => array(),
+ 'properties' => array( 'filter' ),
+ ),
+ array(
+ 'path' => array( 'typography', 'fontSizes' ),
+ 'prevent_override' => false,
+ 'use_default_presets' => true,
+ 'use_default_names' => true,
+ 'value_key' => 'size',
+ 'css_vars' => '--wp--preset--font-size--$slug',
+ 'classes' => array( '.has-$slug-font-size' => 'font-size' ),
+ 'properties' => array( 'font-size' ),
+ ),
+ array(
+ 'path' => array( 'typography', 'fontFamilies' ),
+ 'prevent_override' => false,
+ 'use_default_presets' => true,
+ 'use_default_names' => false,
+ 'value_key' => 'fontFamily',
+ 'css_vars' => '--wp--preset--font-family--$slug',
+ 'classes' => array( '.has-$slug-font-family' => 'font-family' ),
+ 'properties' => array( 'font-family' ),
+ ),
+ array(
+ 'type' => 'layout',
+ 'path' => array( 'layout', 'types' ),
+ 'prevent_override' => false,
+ 'use_default_presets' => true,
+ 'use_default_names' => false,
+ 'value_func' => 'gutenberg_get_layout_preset_styles',
+ 'classes' => array(),
+ ),
+ );
+
+ /**
+ * The valid properties under the settings key.
+ *
+ * @var array
+ */
+ const VALID_SETTINGS = array(
+ 'appearanceTools' => null,
+ 'border' => array(
+ 'color' => null,
+ 'radius' => null,
+ 'style' => null,
+ 'width' => null,
+ ),
+ 'color' => array(
+ 'background' => null,
+ 'custom' => null,
+ 'customDuotone' => null,
+ 'customGradient' => null,
+ 'defaultDuotone' => null,
+ 'defaultGradients' => null,
+ 'defaultPalette' => null,
+ 'duotone' => null,
+ 'gradients' => null,
+ 'link' => null,
+ 'palette' => null,
+ 'text' => null,
+ ),
+ 'custom' => null,
+ 'layout' => array(
+ 'contentSize' => null,
+ 'wideSize' => null,
+ 'types' => null,
+ ),
+ 'spacing' => array(
+ 'blockGap' => null,
+ 'margin' => null,
+ 'padding' => null,
+ 'units' => null,
+ ),
+ 'typography' => array(
+ 'customFontSize' => null,
+ 'dropCap' => null,
+ 'fontFamilies' => null,
+ 'fontSizes' => null,
+ 'fontStyle' => null,
+ 'fontWeight' => null,
+ 'letterSpacing' => null,
+ 'lineHeight' => null,
+ 'textDecoration' => null,
+ 'textTransform' => null,
+ ),
+ );
/**
* The top-level keys a theme.json can have.
diff --git a/lib/load.php b/lib/load.php
index c6619708df3df..d4db8634b993f 100644
--- a/lib/load.php
+++ b/lib/load.php
@@ -121,6 +121,11 @@ function gutenberg_is_experiment_enabled( $name ) {
require __DIR__ . '/global-styles.php';
require __DIR__ . '/pwa.php';
+// TODO: Before this PR merges, move this to be a part of the style engine package.
+// Part of the build process should be to copy the PHP file to the correct location,
+// similar to the loading behaviour in `blocks.php`.
+require __DIR__ . '/class-wp-style-engine-gutenberg.php';
+
require __DIR__ . '/block-supports/elements.php';
require __DIR__ . '/block-supports/colors.php';
require __DIR__ . '/block-supports/typography.php';
From dca45e81505550d806970f0e72727c80b46a12d4 Mon Sep 17 00:00:00 2001
From: Andrew Serong <14988353+andrewserong@users.noreply.github.com>
Date: Tue, 15 Mar 2022 17:17:32 +1100
Subject: [PATCH 2/6] Move style definitions to theme.json
---
lib/block-supports/layout.php | 107 ++++--------------------
lib/class-wp-style-engine-gutenberg.php | 2 +-
lib/compat/wordpress-5.9/theme.json | 58 ++++++++++++-
3 files changed, 73 insertions(+), 94 deletions(-)
diff --git a/lib/block-supports/layout.php b/lib/block-supports/layout.php
index 305e41858b67c..3c74b9c39d98b 100644
--- a/lib/block-supports/layout.php
+++ b/lib/block-supports/layout.php
@@ -25,106 +25,31 @@ function gutenberg_register_layout_support( $block_type ) {
}
}
-function gutenberg_generate_common_flow_layout_styles() {
- $style_engine = WP_Style_Engine_Gutenberg::get_instance();
-
- $style_engine->add_style(
- 'wp-layout-flow',
- array(
- 'selector' => '.alignleft',
- 'rules' => array(
- 'float' => 'left',
- 'margin-right' => '2em',
- 'margin-left' => '0',
- ),
- )
- );
-
- $style_engine->add_style(
- 'wp-layout-flow',
- array(
- 'selector' => '.alignright',
- 'rules' => array(
- 'float' => 'right',
- 'margin-left' => '2em',
- 'margin-right' => '0',
- ),
- )
- );
-
- $style_engine->add_style(
- 'wp-layout-flow',
- array(
- 'selector' => '> .alignfull',
- 'rules' => array(
- 'max-width' => 'none',
- ),
- )
- );
-
- $style_engine->add_style(
- 'wp-layout-flow--global-gap',
- array(
- 'selector' => '> *',
- 'rules' => array(
- 'margin-top' => '0',
- 'margin-bottom' => '0',
- ),
- )
- );
-
- $style_engine->add_style(
- 'wp-layout-flow--global-gap',
- array(
- 'selector' => '> * + *',
- 'rules' => array(
- 'margin-top' => 'var( --wp--style--block-gap )',
- 'margin-bottom' => '0',
- ),
- )
- );
-}
-
-function gutenberg_generate_common_flex_layout_styles() {
- $style_engine = WP_Style_Engine_Gutenberg::get_instance();
-
- $style_engine->add_style(
- 'wp-layout-flex',
- array(
- 'rules' => array(
- 'display' => 'flex',
- 'gap' => '0.5em',
- ),
- )
- );
-
- $style_engine->add_style(
- 'wp-layout-flex',
- array(
- 'selector' => '> *',
- 'rules' => array(
- 'margin' => '0',
- ),
- )
- );
-}
-
function gutenberg_get_layout_preset_styles( $preset_metadata, $presets_by_origin ) {
- WP_Style_Engine_Gutenberg::get_instance()->reset();
+ $style_engine = WP_Style_Engine_Gutenberg::get_instance();
+ $style_engine->reset();
$presets = end( $presets_by_origin );
foreach ( $presets as $preset ) {
- if ( isset( $preset['type'] ) ) {
- if ( 'flow' === $preset['type'] ) {
- $output_styles = gutenberg_generate_common_flow_layout_styles();
- } else if ( 'flex' === $preset['type'] ) {
- $output_styles = gutenberg_generate_common_flex_layout_styles();
+ if ( ! empty( $preset['type'] ) && ! empty( $preset['styles'] ) ) {
+ $slug = ! empty( $preset['slug'] ) ? $preset['slug'] : $preset['type'];
+ $base_class = 'wp-layout-' . sanitize_title( $slug );
+
+ foreach ( $preset['styles'] as $style ) {
+ if ( ! empty( $style['rules'] ) && is_array( $style['rules'] ) ) {
+ $options = array(
+ 'selector' => ! empty( $style['selector'] ) ? $style['selector'] : null,
+ 'suffix' => ! empty( $style['suffix'] ) ? sanitize_title( $style['suffix'] ) : null,
+ 'rules' => $style['rules'],
+ );
+ $style_engine->add_style( $base_class, $options );
+ }
}
}
}
- return WP_Style_Engine_Gutenberg::get_instance()->get_generated_styles();
+ return $style_engine->get_generated_styles();
}
/**
diff --git a/lib/class-wp-style-engine-gutenberg.php b/lib/class-wp-style-engine-gutenberg.php
index 998a7b54fe60d..783220385c11b 100644
--- a/lib/class-wp-style-engine-gutenberg.php
+++ b/lib/class-wp-style-engine-gutenberg.php
@@ -91,7 +91,7 @@ public function get_generated_styles() {
* @return string The class name for the added style.
*/
public function add_style( $key, $options ) {
- $class = ! empty( $options['suffix'] ) ? $key . '-' . sanitize_title( $options['suffix'] ) : $key;
+ $class = ! empty( $options['suffix'] ) ? $key . '--' . sanitize_title( $options['suffix'] ) : $key;
$selector = ! empty( $options['selector'] ) ? ' ' . trim( $options['selector'] ) : '';
$rules = ! empty( $options['rules'] ) ? $options['rules'] : array();
$prefix = ! empty( $options['prefix'] ) ? $options['prefix'] : '.';
diff --git a/lib/compat/wordpress-5.9/theme.json b/lib/compat/wordpress-5.9/theme.json
index b1c0b706a6088..fdbcbe148ba2d 100644
--- a/lib/compat/wordpress-5.9/theme.json
+++ b/lib/compat/wordpress-5.9/theme.json
@@ -189,11 +189,65 @@
"types": [
{
"slug": "flow",
- "type": "flow"
+ "type": "flow",
+ "styles": [
+ {
+ "selector": ".alignleft",
+ "rules": {
+ "float": "left",
+ "margin-right": "2em",
+ "margin-left": "0"
+ }
+ },
+ {
+ "selector": ".alignright",
+ "rules": {
+ "float": "right",
+ "margin-left": "2em",
+ "margin-right": "0"
+ }
+ },
+ {
+ "selector": "> .alignfull",
+ "rules": {
+ "max-width": "none"
+ }
+ },
+ {
+ "suffix": "global-gap",
+ "selector": "> *",
+ "rules": {
+ "margin-top": "0",
+ "margin-bottom": "0"
+ }
+ },
+ {
+ "suffix": "global-gap",
+ "selector": "> * + *",
+ "rules": {
+ "margin-top": "var( --wp--style--block-gap )",
+ "margin-bottom": 0
+ }
+ }
+ ]
},
{
"slug": "flex",
- "type": "flex"
+ "type": "flex",
+ "styles": [
+ {
+ "rules": {
+ "display": "flex",
+ "gap": "0.5em"
+ }
+ },
+ {
+ "selector": "> *",
+ "rules": {
+ "margin": "0"
+ }
+ }
+ ]
}
]
},
From c2780bfbb1887d19f752338a3d65ecda35bffed9 Mon Sep 17 00:00:00 2001
From: Andrew Serong <14988353+andrewserong@users.noreply.github.com>
Date: Tue, 15 Mar 2022 17:33:31 +1100
Subject: [PATCH 3/6] Add classnames to output
---
lib/block-supports/layout.php | 26 +++++++++++++++-----------
lib/compat/wordpress-5.9/theme.json | 19 +++++++++++++------
2 files changed, 28 insertions(+), 17 deletions(-)
diff --git a/lib/block-supports/layout.php b/lib/block-supports/layout.php
index 3c74b9c39d98b..aec61ddd24292 100644
--- a/lib/block-supports/layout.php
+++ b/lib/block-supports/layout.php
@@ -60,13 +60,17 @@ function gutenberg_get_layout_preset_styles( $preset_metadata, $presets_by_origi
* @param boolean $has_block_gap_support Whether the theme has support for the block gap.
* @param string $gap_value The block gap value to apply.
*
- * @return string CSS style.
+ * @return array CSS style and CSS classes.
*/
function gutenberg_get_layout_style( $selector, $layout, $has_block_gap_support = false, $gap_value = null ) {
$layout_type = isset( $layout['type'] ) ? $layout['type'] : 'default';
+ $classes = array();
+
$style = '';
if ( 'default' === $layout_type ) {
+ $classes[] = 'wp-layout-flow';
+
$content_size = isset( $layout['contentSize'] ) ? $layout['contentSize'] : '';
$wide_size = isset( $layout['wideSize'] ) ? $layout['wideSize'] : '';
@@ -89,15 +93,15 @@ function gutenberg_get_layout_style( $selector, $layout, $has_block_gap_support
$style .= "$selector .alignfull { max-width: none; }";
}
- $style .= "$selector > .alignleft { float: left; margin-inline-start: 0; margin-inline-end: 2em; }";
- $style .= "$selector > .alignright { float: right; margin-inline-start: 2em; margin-inline-end: 0; }";
- $style .= "$selector > .aligncenter { margin-left: auto !important; margin-right: auto !important; }";
if ( $has_block_gap_support ) {
+ $classes[] = 'wp-layout-flow--global-gap';
$gap_style = $gap_value ? $gap_value : 'var( --wp--style--block-gap )';
$style .= "$selector > * { margin-block-start: 0; margin-block-end: 0; }";
$style .= "$selector > * + * { margin-block-start: $gap_style; margin-block-end: 0; }";
}
} elseif ( 'flex' === $layout_type ) {
+ $classes[] = 'wp-layout-flex';
+
$layout_orientation = isset( $layout['orientation'] ) ? $layout['orientation'] : 'horizontal';
$justify_content_options = array(
@@ -116,7 +120,6 @@ function gutenberg_get_layout_style( $selector, $layout, $has_block_gap_support
'wrap';
$style = "$selector {";
- $style .= 'display: flex;';
if ( $has_block_gap_support ) {
$gap_style = $gap_value ? $gap_value : 'var( --wp--style--block-gap, 0.5em )';
$style .= "gap: $gap_style;";
@@ -143,11 +146,12 @@ function gutenberg_get_layout_style( $selector, $layout, $has_block_gap_support
}
}
$style .= '}';
-
- $style .= "$selector > * { margin: 0; }";
}
- return $style;
+ return array(
+ 'style' => $style,
+ 'classes' => $classes
+ );
}
/**
@@ -183,17 +187,17 @@ function gutenberg_render_layout_support_flag( $block_content, $block ) {
// Regex for CSS value borrowed from `safecss_filter_attr`, and used here
// because we only want to match against the value, not the CSS attribute.
$gap_value = preg_match( '%[\\\(&=}]|/\*%', $gap_value ) ? null : $gap_value;
- $style = gutenberg_get_layout_style( ".$class_name", $used_layout, $has_block_gap_support, $gap_value );
+ $styles = gutenberg_get_layout_style( ".$class_name", $used_layout, $has_block_gap_support, $gap_value );
// This assumes the hook only applies to blocks with a single wrapper.
// I think this is a reasonable limitation for that particular hook.
$content = preg_replace(
'/' . preg_quote( 'class="', '/' ) . '/',
- 'class="' . esc_attr( $class_name ) . ' ',
+ 'class="' . esc_attr( implode( ' ', array_merge( array( $class_name ), $styles['classes'] ) ) ) . ' ',
$block_content,
1
);
- gutenberg_enqueue_block_support_styles( $style );
+ gutenberg_enqueue_block_support_styles( $styles['style'] );
return $content;
}
diff --git a/lib/compat/wordpress-5.9/theme.json b/lib/compat/wordpress-5.9/theme.json
index fdbcbe148ba2d..b2db219f683f8 100644
--- a/lib/compat/wordpress-5.9/theme.json
+++ b/lib/compat/wordpress-5.9/theme.json
@@ -192,19 +192,26 @@
"type": "flow",
"styles": [
{
- "selector": ".alignleft",
+ "selector": "> .alignleft",
"rules": {
"float": "left",
- "margin-right": "2em",
- "margin-left": "0"
+ "margin-inline-end": "2em",
+ "margin-inline-start": "0"
}
},
{
- "selector": ".alignright",
+ "selector": "> .alignright",
"rules": {
"float": "right",
- "margin-left": "2em",
- "margin-right": "0"
+ "margin-inline-start": "2em",
+ "margin-inline-end": "0"
+ }
+ },
+ {
+ "selector": "> .aligncenter",
+ "rules": {
+ "margin-left": "auto !important",
+ "margin-right": "auto !important"
}
},
{
From 8313c319b14d1b5abb6d5d5c68895db67a92606d Mon Sep 17 00:00:00 2001
From: Andrew Serong <14988353+andrewserong@users.noreply.github.com>
Date: Wed, 16 Mar 2022 17:35:19 +1100
Subject: [PATCH 4/6] Try: move controlled sets of styles to theme.json
---
lib/block-supports/layout.php | 48 ++++++++++++++++---------
lib/class-wp-style-engine-gutenberg.php | 3 +-
lib/compat/wordpress-5.9/theme.json | 45 ++++++++++++++++++++---
3 files changed, 73 insertions(+), 23 deletions(-)
diff --git a/lib/block-supports/layout.php b/lib/block-supports/layout.php
index aec61ddd24292..28209dabeea1f 100644
--- a/lib/block-supports/layout.php
+++ b/lib/block-supports/layout.php
@@ -25,25 +25,42 @@ function gutenberg_register_layout_support( $block_type ) {
}
}
-function gutenberg_get_layout_preset_styles( $preset_metadata, $presets_by_origin ) {
+function gutenberg_get_layout_preset_styles( $preset_metadata, $presets ) {
$style_engine = WP_Style_Engine_Gutenberg::get_instance();
$style_engine->reset();
- $presets = end( $presets_by_origin );
-
- foreach ( $presets as $preset ) {
+ foreach ( $presets as $key => $preset ) {
if ( ! empty( $preset['type'] ) && ! empty( $preset['styles'] ) ) {
$slug = ! empty( $preset['slug'] ) ? $preset['slug'] : $preset['type'];
$base_class = 'wp-layout-' . sanitize_title( $slug );
- foreach ( $preset['styles'] as $style ) {
- if ( ! empty( $style['rules'] ) && is_array( $style['rules'] ) ) {
- $options = array(
- 'selector' => ! empty( $style['selector'] ) ? $style['selector'] : null,
- 'suffix' => ! empty( $style['suffix'] ) ? sanitize_title( $style['suffix'] ) : null,
- 'rules' => $style['rules'],
- );
- $style_engine->add_style( $base_class, $options );
+ if ( ! empty( $preset['styles'] ) ) {
+ foreach ( $preset['styles'] as $style ) {
+ if ( ! empty( $style['rules'] ) && is_array( $style['rules'] ) ) {
+ $args = array(
+ 'selector' => ! empty( $style['selector'] ) ? $style['selector'] : null,
+ 'suffix' => ! empty( $style['suffix'] ) ? sanitize_title( $style['suffix'] ) : null,
+ 'rules' => $style['rules'],
+ );
+ $style_engine->add_style( $base_class, $args );
+ }
+ }
+ }
+
+ if ( ! empty( $preset['controlledSets'] ) ) {
+ foreach ( $preset['controlledSets'] as $controlled_set ) {
+ $property = ! empty( $controlled_set['property'] ) ? sanitize_title( $controlled_set['property'] ) : null;
+ $suffix = ! empty( $controlled_set['suffix'] ) ? sanitize_title( $controlled_set['suffix'] ) : null;
+
+ if ( $property && $suffix && ! empty( $controlled_set['options'] ) ) {
+ foreach( $controlled_set['options'] as $option_setting => $option_value ) {
+ $args = array(
+ 'suffix' => array( $suffix, sanitize_title( $option_setting ) ),
+ 'rules' => array( $property => $option_value ),
+ );
+ $style_engine->add_style( $base_class, $args );
+ }
+ }
}
}
}
@@ -90,13 +107,11 @@ function gutenberg_get_layout_style( $selector, $layout, $has_block_gap_support
$style .= '}';
$style .= "$selector > .alignwide { max-width: " . esc_html( $wide_max_width_value ) . ';}';
- $style .= "$selector .alignfull { max-width: none; }";
}
if ( $has_block_gap_support ) {
$classes[] = 'wp-layout-flow--global-gap';
$gap_style = $gap_value ? $gap_value : 'var( --wp--style--block-gap )';
- $style .= "$selector > * { margin-block-start: 0; margin-block-end: 0; }";
$style .= "$selector > * + * { margin-block-start: $gap_style; margin-block-end: 0; }";
}
} elseif ( 'flex' === $layout_type ) {
@@ -123,12 +138,11 @@ function gutenberg_get_layout_style( $selector, $layout, $has_block_gap_support
if ( $has_block_gap_support ) {
$gap_style = $gap_value ? $gap_value : 'var( --wp--style--block-gap, 0.5em )';
$style .= "gap: $gap_style;";
- } else {
- $style .= 'gap: 0.5em;';
}
+
$style .= "flex-wrap: $flex_wrap;";
if ( 'horizontal' === $layout_orientation ) {
- $style .= 'align-items: center;';
+ $classes[] = 'wp-layout-flex--horizontal';
/**
* Add this style only if is not empty for backwards compatibility,
* since we intend to convert blocks that had flex layout implemented
diff --git a/lib/class-wp-style-engine-gutenberg.php b/lib/class-wp-style-engine-gutenberg.php
index 783220385c11b..bde156028a8e6 100644
--- a/lib/class-wp-style-engine-gutenberg.php
+++ b/lib/class-wp-style-engine-gutenberg.php
@@ -91,7 +91,8 @@ public function get_generated_styles() {
* @return string The class name for the added style.
*/
public function add_style( $key, $options ) {
- $class = ! empty( $options['suffix'] ) ? $key . '--' . sanitize_title( $options['suffix'] ) : $key;
+ $suffix = ! empty( $options['suffix'] ) && is_array( $options['suffix'] ) ? implode( '--', $options['suffix'] ) : $options['suffix'];
+ $class = ! empty( $suffix ) ? $key . '--' . sanitize_key( $suffix ) : $key;
$selector = ! empty( $options['selector'] ) ? ' ' . trim( $options['selector'] ) : '';
$rules = ! empty( $options['rules'] ) ? $options['rules'] : array();
$prefix = ! empty( $options['prefix'] ) ? $options['prefix'] : '.';
diff --git a/lib/compat/wordpress-5.9/theme.json b/lib/compat/wordpress-5.9/theme.json
index b2db219f683f8..5faeae8fe004c 100644
--- a/lib/compat/wordpress-5.9/theme.json
+++ b/lib/compat/wordpress-5.9/theme.json
@@ -186,8 +186,8 @@
"text": true
},
"layout": {
- "types": [
- {
+ "types": {
+ "flow": {
"slug": "flow",
"type": "flow",
"styles": [
@@ -238,7 +238,7 @@
}
]
},
- {
+ "flex": {
"slug": "flex",
"type": "flex",
"styles": [
@@ -253,10 +253,45 @@
"rules": {
"margin": "0"
}
+ },
+ {
+ "suffix": "h-justify",
+ "rules": {
+ "align-items": "center"
+ }
}
- ]
+ ],
+ "controlledSets": {
+ "flexWrap": {
+ "suffix": "wrap",
+ "property": "flex-wrap",
+ "options": {
+ "wrap": "wrap",
+ "nowrap": "nowrap"
+ }
+ },
+ "horizontalJustification": {
+ "suffix": "h-justify",
+ "property": "justify-content",
+ "options": {
+ "left": "flex-start",
+ "right": "flex-end",
+ "center": "center",
+ "space-between": "space-between"
+ }
+ },
+ "verticalJustification": {
+ "suffix": "v-justify",
+ "property": "align-items",
+ "options": {
+ "left": "flex-start",
+ "right": "flex-end",
+ "center": "center"
+ }
+ }
+ }
}
- ]
+ }
},
"spacing": {
"blockGap": null,
From c0375f933cd0ccd034974b7f71e839ca4169ac4f Mon Sep 17 00:00:00 2001
From: Andrew Serong <14988353+andrewserong@users.noreply.github.com>
Date: Thu, 17 Mar 2022 16:39:24 +1100
Subject: [PATCH 5/6] Remove additional type key
---
lib/compat/wordpress-5.9/class-wp-theme-json-5-9.php | 2 +-
lib/compat/wordpress-6.0/class-wp-theme-json-gutenberg.php | 1 -
2 files changed, 1 insertion(+), 2 deletions(-)
diff --git a/lib/compat/wordpress-5.9/class-wp-theme-json-5-9.php b/lib/compat/wordpress-5.9/class-wp-theme-json-5-9.php
index 5ed973d570f05..e004058afc1a3 100644
--- a/lib/compat/wordpress-5.9/class-wp-theme-json-5-9.php
+++ b/lib/compat/wordpress-5.9/class-wp-theme-json-5-9.php
@@ -948,7 +948,7 @@ protected static function compute_preset_classes( $settings, $selector, $origins
$stylesheet = '';
foreach ( static::PRESETS_METADATA as $preset_metadata ) {
- if ( isset( $preset_metadata['type'] ) && 'layout' === $preset_metadata['type'] ) {
+ if ( ! empty( $preset_metadata['path'] ) && 'layout' === $preset_metadata['path'][0] ) {
$preset_per_origin = _wp_array_get( $settings, $preset_metadata['path'], array() );
diff --git a/lib/compat/wordpress-6.0/class-wp-theme-json-gutenberg.php b/lib/compat/wordpress-6.0/class-wp-theme-json-gutenberg.php
index 3a293e5433fc7..13ececb0f585f 100644
--- a/lib/compat/wordpress-6.0/class-wp-theme-json-gutenberg.php
+++ b/lib/compat/wordpress-6.0/class-wp-theme-json-gutenberg.php
@@ -122,7 +122,6 @@ class WP_Theme_JSON_Gutenberg extends WP_Theme_JSON_5_9 {
'properties' => array( 'font-family' ),
),
array(
- 'type' => 'layout',
'path' => array( 'layout', 'types' ),
'prevent_override' => false,
'use_default_presets' => true,
From 3b1609cb14fb6d3c81384994ad43ce3cbddc63bd Mon Sep 17 00:00:00 2001
From: Andrew Serong <14988353+andrewserong@users.noreply.github.com>
Date: Thu, 17 Mar 2022 17:26:02 +1100
Subject: [PATCH 6/6] Add in remaining classes, split classes and styles in
get_layout_style so that it's clearer which are still unique values
---
lib/block-supports/layout.php | 75 ++++++++++++-----------------
lib/compat/wordpress-5.9/theme.json | 7 +++
2 files changed, 37 insertions(+), 45 deletions(-)
diff --git a/lib/block-supports/layout.php b/lib/block-supports/layout.php
index 28209dabeea1f..6d6027fb84e96 100644
--- a/lib/block-supports/layout.php
+++ b/lib/block-supports/layout.php
@@ -70,7 +70,7 @@ function gutenberg_get_layout_preset_styles( $preset_metadata, $presets ) {
}
/**
- * Generates the CSS corresponding to the provided layout.
+ * Generates the CSS and classes corresponding to the provided layout.
*
* @param string $selector CSS selector.
* @param array $layout Layout object. The one that is passed has already checked the existence of default block layout.
@@ -82,12 +82,37 @@ function gutenberg_get_layout_preset_styles( $preset_metadata, $presets ) {
function gutenberg_get_layout_style( $selector, $layout, $has_block_gap_support = false, $gap_value = null ) {
$layout_type = isset( $layout['type'] ) ? $layout['type'] : 'default';
+ // Generate classes for preset styles.
$classes = array();
-
- $style = '';
if ( 'default' === $layout_type ) {
$classes[] = 'wp-layout-flow';
+ if ( $has_block_gap_support ) {
+ $classes[] = 'wp-layout-flow--global-gap';
+ }
+ } else if ( 'flex' === $layout_type ) {
+ $classes[] = 'wp-layout-flex';
+
+ $layout_orientation = isset( $layout['orientation'] ) ? $layout['orientation'] : 'horizontal';
+ $orientation_key = 'horizontal' === $layout_orientation ? 'h-justify' : 'v-justify';
+
+ $classes[] = "wp-layout-flex--$orientation_key";
+
+ if ( ! empty( $layout['flexWrap'] ) ) {
+ $classes[] = 'wp-layout-flex--wrap--' . sanitize_key( $layout['flexWrap'] );
+ }
+
+ if ( ! empty( $layout['justifyContent'] ) ) {
+ $classes[] = sprintf(
+ 'wp-layout-flex--%s--%s',
+ $orientation_key,
+ sanitize_key( $layout['justifyContent'] )
+ );
+ }
+ }
+ // Generate one-off style values.
+ $style = '';
+ if ( 'default' === $layout_type ) {
$content_size = isset( $layout['contentSize'] ) ? $layout['contentSize'] : '';
$wide_size = isset( $layout['wideSize'] ) ? $layout['wideSize'] : '';
@@ -110,56 +135,16 @@ function gutenberg_get_layout_style( $selector, $layout, $has_block_gap_support
}
if ( $has_block_gap_support ) {
- $classes[] = 'wp-layout-flow--global-gap';
$gap_style = $gap_value ? $gap_value : 'var( --wp--style--block-gap )';
$style .= "$selector > * + * { margin-block-start: $gap_style; margin-block-end: 0; }";
}
} elseif ( 'flex' === $layout_type ) {
- $classes[] = 'wp-layout-flex';
-
- $layout_orientation = isset( $layout['orientation'] ) ? $layout['orientation'] : 'horizontal';
-
- $justify_content_options = array(
- 'left' => 'flex-start',
- 'right' => 'flex-end',
- 'center' => 'center',
- );
-
- if ( 'horizontal' === $layout_orientation ) {
- $justify_content_options += array( 'space-between' => 'space-between' );
- }
-
- $flex_wrap_options = array( 'wrap', 'nowrap' );
- $flex_wrap = ! empty( $layout['flexWrap'] ) && in_array( $layout['flexWrap'], $flex_wrap_options, true ) ?
- $layout['flexWrap'] :
- 'wrap';
-
- $style = "$selector {";
if ( $has_block_gap_support ) {
+ $style = "$selector {";
$gap_style = $gap_value ? $gap_value : 'var( --wp--style--block-gap, 0.5em )';
$style .= "gap: $gap_style;";
+ $style .= '}';
}
-
- $style .= "flex-wrap: $flex_wrap;";
- if ( 'horizontal' === $layout_orientation ) {
- $classes[] = 'wp-layout-flex--horizontal';
- /**
- * Add this style only if is not empty for backwards compatibility,
- * since we intend to convert blocks that had flex layout implemented
- * by custom css.
- */
- if ( ! empty( $layout['justifyContent'] ) && array_key_exists( $layout['justifyContent'], $justify_content_options ) ) {
- $style .= "justify-content: {$justify_content_options[ $layout['justifyContent'] ]};";
- }
- } else {
- $style .= 'flex-direction: column;';
- if ( ! empty( $layout['justifyContent'] ) && array_key_exists( $layout['justifyContent'], $justify_content_options ) ) {
- $style .= "align-items: {$justify_content_options[ $layout['justifyContent'] ]};";
- } else {
- $style .= 'align-items: flex-start;';
- }
- }
- $style .= '}';
}
return array(
diff --git a/lib/compat/wordpress-5.9/theme.json b/lib/compat/wordpress-5.9/theme.json
index 5faeae8fe004c..c683e226ff458 100644
--- a/lib/compat/wordpress-5.9/theme.json
+++ b/lib/compat/wordpress-5.9/theme.json
@@ -259,6 +259,13 @@
"rules": {
"align-items": "center"
}
+ },
+ {
+ "suffix": "v-justify",
+ "rules": {
+ "align-items": "flex-start",
+ "flex-direction": "column"
+ }
}
],
"controlledSets": {