diff --git a/docs/reference-guides/theme-json-reference/theme-json-living.md b/docs/reference-guides/theme-json-reference/theme-json-living.md index ac2d059a1aa0f7..fd4c9c7113900b 100644 --- a/docs/reference-guides/theme-json-reference/theme-json-living.md +++ b/docs/reference-guides/theme-json-reference/theme-json-living.md @@ -45,6 +45,7 @@ Settings related to colors. | custom | boolean | true | | | customDuotone | boolean | true | | | customGradient | boolean | true | | +| defaultDuotone | boolean | true | | | defaultGradients | boolean | true | | | defaultPalette | boolean | true | | | duotone | array | | colors, name, slug | 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 76bc1c8b4077ad..5ad476b7d13bbf 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 @@ -69,79 +69,93 @@ class WP_Theme_JSON_5_9 { * * This contains the necessary metadata to process them: * - * - path => where to find the preset within the settings section - * - override => whether a theme preset with the same slug as a default preset - * can override it - * - 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. + * - 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' ), - 'override' => array( 'color', 'defaultPalette' ), - 'use_default_names' => false, - 'value_key' => 'color', - 'css_vars' => '--wp--preset--color--$slug', - 'classes' => 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' ), + 'properties' => array( 'color', 'background-color', 'border-color' ), ), array( - 'path' => array( 'color', 'gradients' ), - 'override' => 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' ), + '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' ), - 'override' => true, - 'use_default_names' => false, - 'value_func' => 'gutenberg_get_duotone_filter_property', - 'css_vars' => '--wp--preset--duotone--$slug', - 'classes' => array(), - 'properties' => array( 'filter' ), + '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' ), - 'override' => 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' ), + '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' ), - 'override' => 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' ), + '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' ), ), ); @@ -229,6 +243,7 @@ class WP_Theme_JSON_5_9 { 'custom' => null, 'customDuotone' => null, 'customGradient' => null, + 'defaultDuotone' => null, 'defaultGradients' => null, 'defaultPalette' => null, 'duotone' => null, @@ -1021,9 +1036,14 @@ protected static function scope_selector( $scope, $selector ) { protected static function get_settings_values_by_slug( $settings, $preset_metadata, $origins ) { $preset_per_origin = _wp_array_get( $settings, $preset_metadata['path'], array() ); + $skip_default_presets = ! static::get_metadata_boolean( $settings, $preset_metadata['use_default_presets'], true ); + $result = array(); foreach ( $origins as $origin ) { - if ( ! isset( $preset_per_origin[ $origin ] ) ) { + if ( + ! isset( $preset_per_origin[ $origin ] ) || + ( 'default' === $origin && $skip_default_presets ) + ) { continue; } foreach ( $preset_per_origin[ $origin ] as $preset ) { @@ -1065,9 +1085,14 @@ protected static function get_settings_slugs( $settings, $preset_metadata, $orig $preset_per_origin = _wp_array_get( $settings, $preset_metadata['path'], array() ); + $skip_default_presets = ! static::get_metadata_boolean( $settings, $preset_metadata['use_default_presets'], true ); + $result = array(); foreach ( $origins as $origin ) { - if ( ! isset( $preset_per_origin[ $origin ] ) ) { + if ( + ! isset( $preset_per_origin[ $origin ] ) || + ( 'default' === $origin && $skip_default_presets ) + ) { continue; } foreach ( $preset_per_origin[ $origin ] as $preset ) { @@ -1470,7 +1495,7 @@ public function merge( $incoming ) { // Replace the presets. foreach ( static::PRESETS_METADATA as $preset ) { - $override_preset = static::should_override_preset( $this->theme_json, $node['path'], $preset['override'] ); + $override_preset = ! static::get_metadata_boolean( $this->theme_json['settings'], $preset['prevent_override'], true ); foreach ( static::VALID_ORIGINS as $origin ) { $base_path = array_merge( $node['path'], $preset['path'] ); @@ -1517,6 +1542,7 @@ public function get_svg_filters( $origins ) { $blocks_metadata = static::get_blocks_metadata(); $setting_nodes = static::get_setting_nodes( $this->theme_json, $blocks_metadata ); + $filters = ''; foreach ( $setting_nodes as $metadata ) { $node = _wp_array_get( $this->theme_json, $metadata['path'], array() ); if ( empty( $node['color']['duotone'] ) ) { @@ -1525,9 +1551,11 @@ public function get_svg_filters( $origins ) { $duotone_presets = $node['color']['duotone']; - $filters = ''; foreach ( $origins as $origin ) { - if ( ! isset( $duotone_presets[ $origin ] ) ) { + if ( + ! isset( $duotone_presets[ $origin ] ) || + ( 'default' === $origin && false === $node['color']['defaultDuotone'] ) + ) { continue; } foreach ( $duotone_presets[ $origin ] as $duotone_preset ) { @@ -1540,42 +1568,40 @@ public function get_svg_filters( $origins ) { } /** - * Returns whether a presets should be overriden or not. + * For metadata values that can either be booleans or paths to booleans, gets the value. + * + * ```php + * $data = array( + * 'color' => array( + * 'defaultPalette' => true + * ) + * ); + * + * static::get_metadata_boolean( $data, false ); + * // => false + * + * static::get_metadata_boolean( $data, array( 'color', 'defaultPalette' ) ); + * // => true + * ``` * - * @param array $theme_json The theme.json like structure to inspect. - * @param array $path Path to inspect. - * @param bool|array $override Data to compute whether to override the preset. + * @param array $data The data to inspect. + * @param bool|array $path Boolean or path to a boolean. + * @param bool $default Default value if the referenced path is missing. * @return boolean */ - protected static function should_override_preset( $theme_json, $path, $override ) { - if ( is_bool( $override ) ) { - return $override; + protected static function get_metadata_boolean( $data, $path, $default = false ) { + if ( is_bool( $path ) ) { + return $path; } - // 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. - if ( is_array( $override ) ) { - $value = _wp_array_get( $theme_json, array_merge( $path, $override ) ); + if ( is_array( $path ) ) { + $value = _wp_array_get( $data, $path ); if ( isset( $value ) ) { - return ! $value; + return $value; } - - // Search the top-level key if none was found for this node. - $value = _wp_array_get( $theme_json, array_merge( array( 'settings' ), $override ) ); - if ( isset( $value ) ) { - return ! $value; - } - - return true; } + + return $default; } /** diff --git a/lib/compat/wordpress-5.9/class-wp-theme-json-resolver-5-9.php b/lib/compat/wordpress-5.9/class-wp-theme-json-resolver-5-9.php index c16f64f46cee6d..7973810d33f65f 100644 --- a/lib/compat/wordpress-5.9/class-wp-theme-json-resolver-5-9.php +++ b/lib/compat/wordpress-5.9/class-wp-theme-json-resolver-5-9.php @@ -190,6 +190,9 @@ public static function get_theme_data( $deprecated = array() ) { $default_gradients = true; } $theme_support_data['settings']['color']['defaultGradients'] = $default_gradients; + + // Classic themes without a theme.json don't support global duotone. + $theme_support_data['settings']['color']['defaultDuotone'] = false; } $with_theme_supports = new WP_Theme_JSON_Gutenberg( $theme_support_data ); $with_theme_supports->merge( static::$theme ); diff --git a/lib/compat/wordpress-5.9/render-svg-filters.php b/lib/compat/wordpress-5.9/render-svg-filters.php index 8d842c7e79fcc0..f13502b867fe95 100644 --- a/lib/compat/wordpress-5.9/render-svg-filters.php +++ b/lib/compat/wordpress-5.9/render-svg-filters.php @@ -5,44 +5,34 @@ * @package gutenberg */ -/* - * If wp_global_styles_render_svg_filters is defined, it means the plugin - * is running on WordPress 5.9.1, so don't need to render the global styles - * SVG filters as it was already done by WordPress core. +/** + * Render the SVG filters supplied by theme.json. + * + * Note that this doesn't render the per-block user-defined + * filters which are handled by wp_render_duotone_support, + * but it should be rendered before the filtered content + * in the body to satisfy Safari's rendering quirks. */ -if ( ! function_exists( 'wp_global_styles_render_svg_filters' ) ) { - /** - * Render the SVG filters supplied by theme.json. - * - * Note that this doesn't render the per-block user-defined - * filters which are handled by wp_render_duotone_support, - * but it should be rendered before the filtered content - * in the body to satisfy Safari's rendering quirks. +function gutenberg_global_styles_render_svg_filters() { + /* + * When calling via the in_admin_header action, we only want to render the + * SVGs on block editor pages. */ - function wp_global_styles_render_svg_filters() { - /* - * When calling via the in_admin_header action, we only want to render the - * SVGs on block editor pages. - */ - if ( - is_admin() && - ! get_current_screen()->is_block_editor() - ) { - return; - } - - $filters = wp_get_global_styles_svg_filters(); - if ( ! empty( $filters ) ) { - echo $filters; - } + if ( + is_admin() && + ! get_current_screen()->is_block_editor() + ) { + return; } - add_action( - 'wp_body_open', - 'wp_global_styles_render_svg_filters' - ); - add_action( - 'in_admin_header', - 'wp_global_styles_render_svg_filters' - ); + $filters = gutenberg_get_global_styles_svg_filters(); + if ( ! empty( $filters ) ) { + echo $filters; + } } + +// Override actions introduced in 5.9.1 if they exist. +remove_action( 'wp_body_open', 'wp_global_styles_render_svg_filters' ); +remove_action( 'in_admin_header', 'wp_global_styles_render_svg_filters' ); +add_action( 'wp_body_open', 'gutenberg_global_styles_render_svg_filters' ); +add_action( 'in_admin_header', 'gutenberg_global_styles_render_svg_filters' ); diff --git a/lib/compat/wordpress-5.9/theme.json b/lib/compat/wordpress-5.9/theme.json index ec29439d7f13f2..7691aa4a64e6a9 100644 --- a/lib/compat/wordpress-5.9/theme.json +++ b/lib/compat/wordpress-5.9/theme.json @@ -13,46 +13,47 @@ "custom": true, "customDuotone": true, "customGradient": true, + "defaultDuotone": true, "defaultGradients": true, "defaultPalette": true, "duotone": [ { - "name": "Dark grayscale" , + "name": "Dark grayscale", "colors": [ "#000000", "#7f7f7f" ], "slug": "dark-grayscale" }, { - "name": "Grayscale" , + "name": "Grayscale", "colors": [ "#000000", "#ffffff" ], "slug": "grayscale" }, { - "name": "Purple and yellow" , + "name": "Purple and yellow", "colors": [ "#8c00b7", "#fcff41" ], "slug": "purple-yellow" }, { - "name": "Blue and red" , + "name": "Blue and red", "colors": [ "#000097", "#ff4747" ], "slug": "blue-red" }, { - "name": "Midnight" , + "name": "Midnight", "colors": [ "#000000", "#00a5ff" ], "slug": "midnight" }, { - "name": "Magenta and yellow" , + "name": "Magenta and yellow", "colors": [ "#c7005a", "#fff278" ], "slug": "magenta-yellow" }, { - "name": "Purple and green" , + "name": "Purple and green", "colors": [ "#a60072", "#67ff66" ], "slug": "purple-green" }, { - "name": "Blue and orange" , + "name": "Blue and orange", "colors": [ "#1900d8", "#ffa96b" ], "slug": "blue-orange" } diff --git a/lib/compat/wordpress-6.0/get-global-styles-and-settings.php b/lib/compat/wordpress-6.0/get-global-styles-and-settings.php index 5e78029d0d1915..bb8819ec2ef257 100644 --- a/lib/compat/wordpress-6.0/get-global-styles-and-settings.php +++ b/lib/compat/wordpress-6.0/get-global-styles-and-settings.php @@ -134,44 +134,42 @@ function gutenberg_get_global_stylesheet( $types = array() ) { return $stylesheet; } -if ( ! function_exists( 'wp_get_global_styles_svg_filters' ) ) { - /** - * Returns a string containing the SVGs to be referenced as filters (duotone). - * - * @return string - */ - function wp_get_global_styles_svg_filters() { - // Return cached value if it can be used and exists. - // It's cached by theme to make sure that theme switching clears the cache. - $transient_name = 'gutenberg_global_styles_svg_filters_' . get_stylesheet(); - $can_use_cached = ( - ( ! defined( 'WP_DEBUG' ) || ! WP_DEBUG ) && - ( ! defined( 'SCRIPT_DEBUG' ) || ! SCRIPT_DEBUG ) && - ( ! defined( 'REST_REQUEST' ) || ! REST_REQUEST ) && - ! is_admin() - ); - if ( $can_use_cached ) { - $cached = get_transient( $transient_name ); - if ( $cached ) { - return $cached; - } +/** + * Returns a string containing the SVGs to be referenced as filters (duotone). + * + * @return string + */ +function gutenberg_get_global_styles_svg_filters() { + // Return cached value if it can be used and exists. + // It's cached by theme to make sure that theme switching clears the cache. + $transient_name = 'gutenberg_global_styles_svg_filters_' . get_stylesheet(); + $can_use_cached = ( + ( ! defined( 'WP_DEBUG' ) || ! WP_DEBUG ) && + ( ! defined( 'SCRIPT_DEBUG' ) || ! SCRIPT_DEBUG ) && + ( ! defined( 'REST_REQUEST' ) || ! REST_REQUEST ) && + ! is_admin() + ); + if ( $can_use_cached ) { + $cached = get_transient( $transient_name ); + if ( $cached ) { + return $cached; } + } - $supports_theme_json = WP_Theme_JSON_Resolver_Gutenberg::theme_has_support(); - - $origins = array( 'default', 'theme', 'custom' ); - if ( ! $supports_theme_json ) { - $origins = array( 'default' ); - } + $supports_theme_json = WP_Theme_JSON_Resolver_Gutenberg::theme_has_support(); - $tree = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data(); - $svgs = $tree->get_svg_filters( $origins ); + $origins = array( 'default', 'theme', 'custom' ); + if ( ! $supports_theme_json ) { + $origins = array( 'default' ); + } - if ( $can_use_cached ) { - // Cache for a minute, same as gutenberg_get_global_stylesheet. - set_transient( $transient_name, $svgs, MINUTE_IN_SECONDS ); - } + $tree = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data(); + $svgs = $tree->get_svg_filters( $origins ); - return $svgs; + if ( $can_use_cached ) { + // Cache for a minute, same as gutenberg_get_global_stylesheet. + set_transient( $transient_name, $svgs, MINUTE_IN_SECONDS ); } + + return $svgs; } diff --git a/packages/block-editor/src/hooks/duotone.js b/packages/block-editor/src/hooks/duotone.js index 13de8f2a4d79ac..163846d52e415a 100644 --- a/packages/block-editor/src/hooks/duotone.js +++ b/packages/block-editor/src/hooks/duotone.js @@ -12,7 +12,7 @@ import { getBlockSupport, hasBlockSupport } from '@wordpress/blocks'; import { SVG } from '@wordpress/components'; import { createHigherOrderComponent, useInstanceId } from '@wordpress/compose'; import { addFilter } from '@wordpress/hooks'; -import { useContext, createPortal } from '@wordpress/element'; +import { useMemo, useContext, createPortal } from '@wordpress/element'; /** * Internal dependencies @@ -140,12 +140,36 @@ ${ selector } { ); } +function useMultiOriginPresets( { presetSetting, defaultSetting } ) { + const disableDefault = ! useSetting( defaultSetting ); + const userPresets = + useSetting( `${ presetSetting }.custom` ) || EMPTY_ARRAY; + const themePresets = + useSetting( `${ presetSetting }.theme` ) || EMPTY_ARRAY; + const defaultPresets = + useSetting( `${ presetSetting }.default` ) || EMPTY_ARRAY; + return useMemo( + () => [ + ...userPresets, + ...themePresets, + ...( disableDefault ? EMPTY_ARRAY : defaultPresets ), + ], + [ disableDefault, userPresets, themePresets, defaultPresets ] + ); +} + function DuotonePanel( { attributes, setAttributes } ) { const style = attributes?.style; const duotone = style?.color?.duotone; - const duotonePalette = useSetting( 'color.duotone' ) || EMPTY_ARRAY; - const colorPalette = useSetting( 'color.palette' ) || EMPTY_ARRAY; + const duotonePalette = useMultiOriginPresets( { + presetSetting: 'color.duotone', + defaultSetting: 'color.defaultDuotone', + } ); + const colorPalette = useMultiOriginPresets( { + presetSetting: 'color.palette', + defaultSetting: 'color.defaultPalette', + } ); const disableCustomColors = ! useSetting( 'color.custom' ); const disableCustomDuotone = ! useSetting( 'color.customDuotone' ) || diff --git a/schemas/json/theme.json b/schemas/json/theme.json index a21710fe4626b5..f2d8419f281db2 100644 --- a/schemas/json/theme.json +++ b/schemas/json/theme.json @@ -73,6 +73,11 @@ "type": "boolean", "default": true }, + "defaultDuotone": { + "description": "Allow users to choose filters from the default duotone filter presets.", + "type": "boolean", + "default": true + }, "defaultGradients": { "description": "Allow users to choose colors from the default gradients.", "type": "boolean", @@ -642,7 +647,7 @@ } }, "patternProperties": { - "^[a-z][a-z0-9-]*\/[a-z][a-z0-9-]*$": { + "^[a-z][a-z0-9-]*/[a-z][a-z0-9-]*$": { "$ref": "#/definitions/settingsPropertiesComplete" } },