diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php index 05e9f411f16d8f..5b1cec32d4cc44 100644 --- a/lib/class-wp-theme-json-gutenberg.php +++ b/lib/class-wp-theme-json-gutenberg.php @@ -661,21 +661,28 @@ private static function compute_preset_classes( $settings, $selector ) { // and we don't want to increase its specificity. $selector = ''; } + $origins = array( 'core', 'theme', 'user' ); $stylesheet = ''; foreach ( self::PRESETS_METADATA as $preset ) { - $values = _wp_array_get( $settings, $preset['path'], array() ); - foreach ( $values as $value ) { - foreach ( $preset['classes'] as $class ) { - $stylesheet .= self::to_ruleset( - self::append_to_selector( $selector, '.has-' . $value['slug'] . '-' . $class['class_suffix'] ), - array( + $values_per_origin = _wp_array_get( $settings, $preset['path'], array() ); + foreach ( $origins as $origin ) { + if ( ! isset( $values_per_origin[ $origin ] ) ) { + continue; + } + $values = $values_per_origin[ $origin ]; + foreach ( $values as $value ) { + foreach ( $preset['classes'] as $class ) { + $stylesheet .= self::to_ruleset( + self::append_to_selector( $selector, '.has-' . $value['slug'] . '-' . $class['class_suffix'] ), array( - 'name' => $class['property_name'], - 'value' => $value[ $preset['value_key'] ] . ' !important', - ), - ) - ); + array( + 'name' => $class['property_name'], + 'value' => $value[ $preset['value_key'] ] . ' !important', + ), + ) + ); + } } } } @@ -701,13 +708,20 @@ private static function compute_preset_classes( $settings, $selector ) { */ private static function compute_preset_vars( $settings ) { $declarations = array(); + $origins = array( 'core', 'theme', 'user' ); foreach ( self::PRESETS_METADATA as $preset ) { - $values = _wp_array_get( $settings, $preset['path'], array() ); - foreach ( $values as $value ) { - $declarations[] = array( - 'name' => '--wp--preset--' . $preset['css_var_infix'] . '--' . $value['slug'], - 'value' => $value[ $preset['value_key'] ], - ); + $values_per_origin = _wp_array_get( $settings, $preset['path'], array() ); + foreach ( $origins as $origin ) { + if ( ! isset( $values_per_origin[ $origin ] ) ) { + continue; + } + $values = $values_per_origin[ $origin ]; + foreach ( $values as $value ) { + $declarations[] = array( + 'name' => '--wp--preset--' . $preset['css_var_infix'] . '--' . $value['slug'], + 'value' => $value[ $preset['value_key'] ], + ); + } } } @@ -1104,7 +1118,8 @@ public function get_stylesheet( $type = 'all' ) { * * @param WP_Theme_JSON $incoming Data to merge. */ - public function merge( $incoming ) { + public function merge( $incoming, $origin ) { + $incoming_data = $incoming->get_raw_data(); $this->theme_json = array_replace_recursive( $this->theme_json, $incoming_data ); @@ -1114,12 +1129,14 @@ public function merge( $incoming ) { // // These are the cases that have array values at the leaf levels. $properties = array(); - $properties[] = array( 'color', 'palette' ); - $properties[] = array( 'color', 'gradients' ); + $properties[] = array( 'custom' ); $properties[] = array( 'spacing', 'units' ); - $properties[] = array( 'typography', 'fontSizes' ); - $properties[] = array( 'typography', 'fontFamilies' ); + + $to_append[] = array( 'color', 'palette' ); + $to_append[] = array( 'color', 'gradients' ); + $to_append[] = array( 'typography', 'fontSizes' ); + $to_append[] = array( 'typography', 'fontFamilies' ); $nodes = self::get_setting_nodes( $this->theme_json ); foreach ( $nodes as $metadata ) { @@ -1130,6 +1147,27 @@ public function merge( $incoming ) { gutenberg_experimental_set( $this->theme_json, $path, $node ); } } + + foreach ( $to_append as $property_path ) { + $path = array_merge( $metadata['path'], $property_path ); + $node = _wp_array_get( $incoming_data, $path, null ); + if ( null !== $node ) { + $existing_node = _wp_array_get( $this->theme_json, $path, null ); + $new_node = array_filter( + $existing_node, + function ( $key ) { + return in_array( $key, array( 'core', 'theme', 'user ' ), true ); + }, + ARRAY_FILTER_USE_KEY + ); + if ( isset( $node[ $origin ] ) ) { + $new_node[ $origin ] = $node[ $origin ]; + } else { + $new_node[ $origin ] = $node; + } + gutenberg_experimental_set( $this->theme_json, $path, $new_node ); + } + } } } diff --git a/lib/class-wp-theme-json-resolver-gutenberg.php b/lib/class-wp-theme-json-resolver-gutenberg.php index fcf5da98d8dda9..e34c4fdfad6eb7 100644 --- a/lib/class-wp-theme-json-resolver-gutenberg.php +++ b/lib/class-wp-theme-json-resolver-gutenberg.php @@ -290,7 +290,7 @@ public static function get_theme_data( $theme_support_data = array() ) { * to override the ones declared via add_theme_support. */ $with_theme_supports = new WP_Theme_JSON_Gutenberg( $theme_support_data ); - $with_theme_supports->merge( self::$theme ); + $with_theme_supports->merge( self::$theme, 'theme' ); return $with_theme_supports; } @@ -415,11 +415,11 @@ public static function get_merged_data( $settings = array(), $origin = 'user' ) $theme_support_data = WP_Theme_JSON_Gutenberg::get_from_editor_settings( $settings ); $result = new WP_Theme_JSON_Gutenberg(); - $result->merge( self::get_core_data() ); - $result->merge( self::get_theme_data( $theme_support_data ) ); + $result->merge( self::get_core_data(), 'core' ); + $result->merge( self::get_theme_data( $theme_support_data ), 'theme' ); if ( 'user' === $origin ) { - $result->merge( self::get_user_data() ); + $result->merge( self::get_user_data(), 'user' ); } return $result; diff --git a/lib/global-styles.php b/lib/global-styles.php index 2aa5c1b104538c..927a9fce193273 100644 --- a/lib/global-styles.php +++ b/lib/global-styles.php @@ -137,11 +137,23 @@ function_exists( 'gutenberg_is_edit_site_page' ) && // Copied from get_block_editor_settings() at wordpress-develop/block-editor.php. $settings['__experimentalFeatures'] = $consolidated->get_settings(); if ( isset( $settings['__experimentalFeatures']['color']['palette'] ) ) { - $settings['colors'] = $settings['__experimentalFeatures']['color']['palette']; + $colors_by_origin = $settings['__experimentalFeatures']['color']['palette']; + $settings['colors'] = isset( $colors_by_origin['user'] ) ? + $colors_by_origin['user'] : ( + isset( $colors_by_origin['theme'] ) ? + $colors_by_origin['theme'] : + $colors_by_origin['core'] + ); unset( $settings['__experimentalFeatures']['color']['palette'] ); } if ( isset( $settings['__experimentalFeatures']['color']['gradients'] ) ) { - $settings['gradients'] = $settings['__experimentalFeatures']['color']['gradients']; + $gradients_by_origin = $settings['__experimentalFeatures']['color']['gradients']; + $settings['gradients'] = isset( $gradients_by_origin['user'] ) ? + $gradients_by_origin['user'] : ( + isset( $gradients_by_origin['theme'] ) ? + $gradients_by_origin['theme'] : + $gradients_by_origin['core'] + ); unset( $settings['__experimentalFeatures']['color']['gradients'] ); } if ( isset( $settings['__experimentalFeatures']['color']['custom'] ) ) { @@ -153,7 +165,13 @@ function_exists( 'gutenberg_is_edit_site_page' ) && unset( $settings['__experimentalFeatures']['color']['customGradient'] ); } if ( isset( $settings['__experimentalFeatures']['typography']['fontSizes'] ) ) { - $settings['fontSizes'] = $settings['__experimentalFeatures']['typography']['fontSizes']; + $font_sizes_by_origin = $settings['__experimentalFeatures']['typography']['fontSizes']; + $settings['fontSizes'] = isset( $font_sizes_by_origin['user'] ) ? + $font_sizes_by_origin['user'] : ( + isset( $font_sizes_by_origin['theme'] ) ? + $font_sizes_by_origin['theme'] : + $font_sizes_by_origin['core'] + ); unset( $settings['__experimentalFeatures']['typography']['fontSizes'] ); } if ( isset( $settings['__experimentalFeatures']['typography']['customFontSize'] ) ) { diff --git a/packages/edit-site/src/components/editor/global-styles-provider.js b/packages/edit-site/src/components/editor/global-styles-provider.js index 7fdf78b060a829..05e672dfa05f17 100644 --- a/packages/edit-site/src/components/editor/global-styles-provider.js +++ b/packages/edit-site/src/components/editor/global-styles-provider.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { set, get, mergeWith } from 'lodash'; +import { set, get, mergeWith, mapValues, setWith, clone } from 'lodash'; /** * WordPress dependencies @@ -30,6 +30,7 @@ import { ROOT_BLOCK_SUPPORTS, getValueFromVariable, getPresetVariable, + PRESET_METADATA, } from './utils'; import { toCustomProperties, toStyles } from './global-styles-renderer'; import { store as editSiteStore } from '../../store'; @@ -111,6 +112,10 @@ const getBlockMetadata = ( blockTypes ) => { return result; }; +function immutableSet( object, path, value ) { + return setWith( object ? clone( object ) : {}, path, value, clone ); +} + export default function GlobalStylesProvider( { children, baseStyles } ) { const [ content, setContent ] = useGlobalStylesEntityContent(); const { blockTypes, settings } = useSelect( ( select ) => { @@ -150,12 +155,41 @@ export default function GlobalStylesProvider( { children, baseStyles } ) { newUserStyles = EMPTY_CONTENT; } + const addUserToSettings = ( settingsToAdd ) => { + PRESET_METADATA.forEach( ( { path } ) => { + const presetData = get( settingsToAdd, path ); + if ( presetData ) { + settingsToAdd = immutableSet( settingsToAdd, path, { + user: presetData, + } ); + } + } ); + return settingsToAdd; + }; + + let userStylesWithOrigin = newUserStyles; + if ( userStylesWithOrigin.settings ) { + userStylesWithOrigin = { + ...userStylesWithOrigin, + settings: addUserToSettings( userStylesWithOrigin.settings ), + }; + if ( userStylesWithOrigin.settings.blocks ) { + userStylesWithOrigin.settings = { + ...userStylesWithOrigin.settings, + blocks: mapValues( + userStylesWithOrigin.settings.blocks, + addUserToSettings + ), + }; + } + } + // At this point, the version schema of the theme & user // is the same, so we can merge them. const newMergedStyles = mergeWith( {}, baseStyles, - newUserStyles, + userStylesWithOrigin, mergeTreesCustomizer ); diff --git a/packages/edit-site/src/components/editor/global-styles-renderer.js b/packages/edit-site/src/components/editor/global-styles-renderer.js index 70c284535cf9ae..0af790a60c6619 100644 --- a/packages/edit-site/src/components/editor/global-styles-renderer.js +++ b/packages/edit-site/src/components/editor/global-styles-renderer.js @@ -52,12 +52,17 @@ function getPresetsDeclarations( blockPresets = {} ) { return reduce( PRESET_METADATA, ( declarations, { path, valueKey, cssVarInfix } ) => { - const preset = get( blockPresets, path, [] ); - preset.forEach( ( value ) => { - declarations.push( - `--wp--preset--${ cssVarInfix }--${ value.slug }: ${ value[ valueKey ] }` - ); + const presetByOrigin = get( blockPresets, path, [] ); + [ 'core', 'theme', 'user' ].forEach( ( origin ) => { + if ( presetByOrigin[ origin ] ) { + presetByOrigin[ origin ].forEach( ( value ) => { + declarations.push( + `--wp--preset--${ cssVarInfix }--${ value.slug }: ${ value[ valueKey ] }` + ); + } ); + } } ); + return declarations; }, [] @@ -78,15 +83,19 @@ function getPresetsClasses( blockSelector, blockPresets = {} ) { if ( ! classes ) { return declarations; } - const presets = get( blockPresets, path, [] ); - presets.forEach( ( preset ) => { - classes.forEach( ( { classSuffix, propertyName } ) => { - const slug = preset.slug; - const value = preset[ valueKey ]; - const classSelectorToUse = `.has-${ slug }-${ classSuffix }`; - const selectorToUse = `${ blockSelector }${ classSelectorToUse }`; - declarations += `${ selectorToUse }{${ propertyName }: ${ value } !important;}`; - } ); + const presetByOrigin = get( blockPresets, path, [] ); + [ 'core', 'theme', 'user' ].forEach( ( origin ) => { + if ( presetByOrigin[ origin ] ) { + presetByOrigin[ origin ].forEach( ( preset ) => { + classes.forEach( ( { classSuffix, propertyName } ) => { + const slug = preset.slug; + const value = preset[ valueKey ]; + const classSelectorToUse = `.has-${ slug }-${ classSuffix }`; + const selectorToUse = `${ blockSelector }${ classSelectorToUse }`; + declarations += `${ selectorToUse }{${ propertyName }: ${ value } !important;}`; + } ); + } ); + } } ); return declarations; }, diff --git a/packages/edit-site/src/components/editor/utils.js b/packages/edit-site/src/components/editor/utils.js index 865279b4625ba4..326c1e520c31e4 100644 --- a/packages/edit-site/src/components/editor/utils.js +++ b/packages/edit-site/src/components/editor/utils.js @@ -70,6 +70,11 @@ export const PRESET_METADATA = [ }, ]; +const presetPaths = {}; +forEach( PRESET_METADATA, ( { path } ) => { + presetPaths[ path.join( '.' ) ] = true; +} ); + const STYLE_PROPERTIES_TO_CSS_VAR_INFIX = { backgroundColor: 'color', background: 'gradient', @@ -90,13 +95,29 @@ function getPresetMetadataFromStyleProperty( styleProperty ) { return getPresetMetadataFromStyleProperty.MAP[ styleProperty ]; } +function getHighestPriorityOrigin( presetByOrigin, path ) { + if ( presetByOrigin && presetPaths[ path ] ) { + const origins = [ 'user', 'theme', 'core' ]; + for ( const origin of origins ) { + if ( presetByOrigin[ origin ] ) { + return presetByOrigin[ origin ]; + } + } + return undefined; + } + return presetByOrigin; +} + export function useSetting( path, blockName = '' ) { const settings = useSelect( ( select ) => { return select( editSiteStore ).getSettings(); } ); const topLevelPath = `__experimentalFeatures.${ path }`; const blockPath = `__experimentalFeatures.blocks.${ blockName }.${ path }`; - return get( settings, blockPath ) ?? get( settings, topLevelPath ); + return ( + getHighestPriorityOrigin( get( settings, blockPath ), path ) ?? + getHighestPriorityOrigin( get( settings, topLevelPath ), path ) + ); } export function getPresetVariable( styles, context, propertyName, value ) { diff --git a/packages/edit-site/src/components/sidebar/color-palette-panel.js b/packages/edit-site/src/components/sidebar/color-palette-panel.js index e3fbda55711974..fbae0697b9c811 100644 --- a/packages/edit-site/src/components/sidebar/color-palette-panel.js +++ b/packages/edit-site/src/components/sidebar/color-palette-panel.js @@ -38,14 +38,23 @@ export default function ColorPalettePanel( { ( select ) => { const baseStyles = select( editSiteStore ).getSettings() .__experimentalGlobalStylesBaseStyles; + const contextualBasePalette = get( baseStyles, [ + 'settings', + 'blocks', + contextName, + 'color', + 'palette', + ] ); + const globalPalette = get( baseStyles, [ + 'settings', + 'color', + 'palette', + ] ); const basePalette = - get( baseStyles, [ - 'settings', - 'blocks', - contextName, - 'color', - 'palette', - ] ) ?? get( baseStyles, [ 'settings', 'color', 'palette' ] ); + contextualBasePalette?.theme ?? + contextualBasePalette?.core ?? + globalPalette?.theme ?? + globalPalette?.core; if ( ! basePalette ) { return EMPTY_ARRAY; }