Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Global styles: block background UI controls #60100

Merged
merged 11 commits into from
Jul 5, 2024
3 changes: 3 additions & 0 deletions backport-changelog/6.7/6836.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
https://github.com/WordPress/wordpress-develop/pull/6836

* https://github.com/WordPress/gutenberg/pull/60100
13 changes: 13 additions & 0 deletions docs/reference-guides/theme-json-reference/theme-json-living.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,19 @@ Generate custom CSS custom properties of the form `--wp--custom--{key}--{nested-
## Styles


### background

Background styles.

| Property | Type | Props |
| --- | --- |--- |
| backgroundImage | string, object | |
| backgroundPosition | string, object | |
| backgroundRepeat | string, object | |
| backgroundSize | string, object | |

---

### border

Border styles.
Expand Down
12 changes: 6 additions & 6 deletions lib/block-supports/background.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,14 @@ function gutenberg_render_background_support( $block_content, $block ) {
return $block_content;
}

$background_styles = array();
$background_styles['backgroundImage'] = isset( $block_attributes['style']['background']['backgroundImage'] ) ? $block_attributes['style']['background']['backgroundImage'] : array();
$background_styles = array();
$background_styles['backgroundImage'] = $block_attributes['style']['background']['backgroundImage'] ?? null;
$background_styles['backgroundSize'] = $block_attributes['style']['background']['backgroundSize'] ?? null;
$background_styles['backgroundPosition'] = $block_attributes['style']['background']['backgroundPosition'] ?? null;
$background_styles['backgroundRepeat'] = $block_attributes['style']['background']['backgroundRepeat'] ?? null;

if ( ! empty( $background_styles['backgroundImage'] ) ) {
$background_styles['backgroundSize'] = isset( $block_attributes['style']['background']['backgroundSize'] ) ? $block_attributes['style']['background']['backgroundSize'] : 'cover';
$background_styles['backgroundPosition'] = isset( $block_attributes['style']['background']['backgroundPosition'] ) ? $block_attributes['style']['background']['backgroundPosition'] : null;
$background_styles['backgroundRepeat'] = isset( $block_attributes['style']['background']['backgroundRepeat'] ) ? $block_attributes['style']['background']['backgroundRepeat'] : null;

$background_styles['backgroundSize'] = $background_styles['backgroundSize'] ?? 'cover';
// If the background size is set to `contain` and no position is set, set the position to `center`.
if ( 'contain' === $background_styles['backgroundSize'] && ! $background_styles['backgroundPosition'] ) {
$background_styles['backgroundPosition'] = 'center';
Expand Down
8 changes: 4 additions & 4 deletions lib/class-wp-theme-json-gutenberg.php
Original file line number Diff line number Diff line change
Expand Up @@ -503,10 +503,10 @@ class WP_Theme_JSON_Gutenberg {
*/
const VALID_STYLES = array(
'background' => array(
'backgroundImage' => 'top',
'backgroundPosition' => 'top',
'backgroundRepeat' => 'top',
'backgroundSize' => 'top',
'backgroundImage' => null,
'backgroundPosition' => null,
'backgroundRepeat' => null,
'backgroundSize' => null,
ramonjd marked this conversation as resolved.
Show resolved Hide resolved
),
'border' => array(
'color' => null,
Expand Down
33 changes: 31 additions & 2 deletions lib/class-wp-theme-json-resolver-gutenberg.php
Original file line number Diff line number Diff line change
Expand Up @@ -805,6 +805,7 @@ public static function get_style_variations( $scope = 'theme' ) {
* as the value of `_link` object in REST API responses.
*
* @since 6.6.0
* @since 6.7.0 Added support for resolving block styles.
*
* @param WP_Theme_JSON_Gutenberg $theme_json A theme json instance.
* @return array An array of resolved paths.
Expand All @@ -818,10 +819,11 @@ public static function get_resolved_theme_uris( $theme_json ) {

$theme_json_data = $theme_json->get_raw_data();

// Top level styles.
$background_image_url = $theme_json_data['styles']['background']['backgroundImage']['url'] ?? null;
// Using the same file convention when registering web fonts. See: WP_Font_Face_Resolver:: to_theme_file_uri.
$placeholder = 'file:./';

// Top level styles.
$background_image_url = $theme_json_data['styles']['background']['backgroundImage']['url'] ?? null;
if (
isset( $background_image_url ) &&
is_string( $background_image_url ) &&
Expand All @@ -840,6 +842,33 @@ public static function get_resolved_theme_uris( $theme_json ) {
$resolved_theme_uris[] = $resolved_theme_uri;
}

// Block styles.
if ( ! empty( $theme_json_data['styles']['blocks'] ) ) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably should abstract this logic. There'll be elements and other things later I imagine.

Also add unit tests to cover this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What did you have in mind for the abstraction? A separate method, or function call to another file? Just curious how this part of the feature might evolve 🙂

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe once (if?) we support background images for elements, the whole foreach could be a function that takes an array of either blocks or elements so we can run it on both? I don't think it's worth abstracting until that happens though.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it's worth abstracting until that happens though.

Good point. Let's cross that bridge when we come to it.

What did you have in mind for the abstraction? A separate method, or function call to another file? Just curious how this part of the feature might evolve

I haven't really thought about it, but the obvious target would be around building the resolved array items. So the if block starting from wp_check_filetype().

The future will involve target specific styles — top-level, blocks, elements and, eventually, variations, the latter two requiring possibly further nested loops.

Maybe once elements comes along it'll be clearer how the abstraction should look.

foreach ( $theme_json_data['styles']['blocks'] as $block_name => $block_styles ) {
if ( ! isset( $block_styles['background']['backgroundImage']['url'] ) ) {
continue;
}
$background_image_url = $block_styles['background']['backgroundImage']['url'] ?? null;
if (
isset( $background_image_url ) &&
is_string( $background_image_url ) &&
// Skip if the src doesn't start with the placeholder, as there's nothing to replace.
str_starts_with( $background_image_url, $placeholder ) ) {
$file_type = wp_check_filetype( $background_image_url );
$src_url = str_replace( $placeholder, '', $background_image_url );
$resolved_theme_uri = array(
'name' => $background_image_url,
'href' => sanitize_url( get_theme_file_uri( $src_url ) ),
'target' => "styles.blocks.{$block_name}.background.backgroundImage.url",
);
if ( isset( $file_type['type'] ) ) {
$resolved_theme_uri['type'] = $file_type['type'];
}
$resolved_theme_uris[] = $resolved_theme_uri;
}
}
}

return $resolved_theme_uris;
}

Expand Down
1 change: 1 addition & 0 deletions lib/global-styles-and-settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ function gutenberg_add_global_styles_block_custom_css() {
function gutenberg_add_global_styles_for_blocks() {
global $wp_styles;
$tree = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data();
$tree = WP_Theme_JSON_Resolver_Gutenberg::resolve_theme_file_uris( $tree );
$block_nodes = $tree->get_styles_block_nodes();
foreach ( $block_nodes as $metadata ) {
$block_css = $tree->get_styles_for_block( $metadata );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@ function BackgroundImageControls( {
inheritedValue,
onRemoveImage = noop,
displayInPanel,
themeFileURIs,
} ) {
const mediaUpload = useSelect(
( select ) => select( blockEditorStore ).getSettings().mediaUpload,
Expand Down Expand Up @@ -392,7 +393,10 @@ function BackgroundImageControls( {
name={
<InspectorImagePreviewItem
className="block-editor-global-styles-background-panel__image-preview"
imgUrl={ url }
imgUrl={ getResolvedThemeFilePath(
url,
themeFileURIs
) }
filename={ title }
label={ imgLabel }
/>
Expand Down Expand Up @@ -706,6 +710,7 @@ export default function BackgroundPanel( {
onChange={ onChange }
style={ value }
inheritedValue={ inheritedValue }
themeFileURIs={ themeFileURIs }
displayInPanel
onRemoveImage={ () => {
setIsDropDownOpen( false );
Expand All @@ -727,6 +732,7 @@ export default function BackgroundPanel( {
onChange={ onChange }
style={ value }
inheritedValue={ inheritedValue }
themeFileURIs={ themeFileURIs }
/>
) }
</div>
Expand Down
9 changes: 9 additions & 0 deletions packages/block-editor/src/components/global-styles/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,15 @@ export function useSettingsForBlockElement(
}
} );

[ 'backgroundImage', 'backgroundSize' ].forEach( ( key ) => {
if ( ! supportedStyles.includes( key ) ) {
updatedSettings.background = {
...updatedSettings.background,
[ key ]: false,
};
}
} );

updatedSettings.shadow = supportedStyles.includes( 'shadow' )
? updatedSettings.shadow
: false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { GlobalStylesContext } from './context';
import { useGlobalSetting } from './hooks';
import { getDuotoneFilter } from '../duotone/utils';
import { getGapCSSValue } from '../../hooks/gap';
import { setBackgroundStyleDefaults } from '../../hooks/background';
import { store as blockEditorStore } from '../../store';
import { LAYOUT_DEFINITIONS } from '../../layouts/definitions';
import { getValueFromObjectPath, setImmutably } from '../../utils/object';
Expand Down Expand Up @@ -387,6 +388,20 @@ export function getStylesDeclarations(
[]
);

/*
* Set background defaults.
* Applies to all background styles except the top-level site background.
*/
if ( ! isRoot && !! blockStyles.background ) {
blockStyles = {
...blockStyles,
background: {
...blockStyles.background,
...setBackgroundStyleDefaults( blockStyles.background ),
ramonjd marked this conversation as resolved.
Show resolved Hide resolved
},
};
}

// The goal is to move everything to server side generated engine styles
// This is temporary as we absorb more and more styles into the engine.
const extraRules = getCSSRules( blockStyles );
Expand Down
29 changes: 25 additions & 4 deletions packages/block-editor/src/hooks/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ import {
useHasBackgroundPanel,
hasBackgroundImageValue,
} from '../components/global-styles/background-panel';
import {
globalStylesDataKey,
globalStylesLinksDataKey,
} from '../store/private-keys';

export const BACKGROUND_SUPPORT_KEY = 'background';

Expand Down Expand Up @@ -134,10 +138,25 @@ export function BackgroundImagePanel( {
setAttributes,
settings,
} ) {
const style = useSelect(
( select ) =>
select( blockEditorStore ).getBlockAttributes( clientId )?.style,
[ clientId ]
const { style, inheritedValue, _links } = useSelect(
( select ) => {
const { getBlockAttributes, getSettings } =
select( blockEditorStore );
const _settings = getSettings();
return {
style: getBlockAttributes( clientId )?.style,
_links: _settings[ globalStylesLinksDataKey ],
/*
* @TODO 1. Pass inherited value down to all block style controls,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably doesn't need to happen any time soon, but might be useful for other block controls I imagine?

* See: packages/block-editor/src/hooks/style.js
* @TODO 2. Add support for block style variations,
* See implementation: packages/block-editor/src/hooks/block-style-variation.js
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So if a variation has been applied to a block, we need to parse it here.

This needs to happen in a follow up. I've added a note to #54336

cc @aaronrobertshaw just in case there are gotchas (TL;DR - I'm trying to pass down the theme styles applied to a block to the controls, so that the controls are aware of any defaults)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, at a glance, we'd need to detect which, if any, variation has been applied to the block, then merge the variation's data over the block type's data. The resulting set would tell us which value the control should get.

*/
inheritedValue:
_settings[ globalStylesDataKey ]?.blocks?.[ name ],
};
},
[ clientId, name ]
);

if (
Expand Down Expand Up @@ -165,12 +184,14 @@ export function BackgroundImagePanel( {

return (
<StylesBackgroundPanel
inheritedValue={ inheritedValue }
as={ BackgroundInspectorControl }
panelId={ clientId }
defaultValues={ BACKGROUND_DEFAULT_VALUES }
settings={ updatedSettings }
onChange={ onChange }
value={ style }
themeFileURIs={ _links?.[ 'wp:theme-file' ] }
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the magic that allows the background image preview to display where the image comes from theme.json

/>
);
}
Expand Down
1 change: 1 addition & 0 deletions packages/block-editor/src/hooks/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,5 +87,6 @@ export { getSpacingClassesAndStyles } from './use-spacing-props';
export { getTypographyClassesAndStyles } from './use-typography-props';
export { getGapCSSValue } from './gap';
export { useCachedTruthy } from './use-cached-truthy';
export { setBackgroundStyleDefaults } from './background';
export { useZoomOut } from './use-zoom-out';
export { __unstableBlockStyleVariationOverridesWithConfig } from './block-style-variation';
4 changes: 4 additions & 0 deletions packages/block-editor/src/private-apis.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import BlockQuickNavigation from './components/block-quick-navigation';
import { LayoutStyle } from './components/block-list/layout';
import { BlockRemovalWarningModal } from './components/block-removal-warning-modal';
import {
setBackgroundStyleDefaults,
useLayoutClasses,
useLayoutStyles,
__unstableBlockStyleVariationOverridesWithConfig,
Expand All @@ -40,6 +41,7 @@ import {
selectBlockPatternsKey,
reusableBlocksSelectKey,
globalStylesDataKey,
globalStylesLinksDataKey,
} from './store/private-keys';
import { requiresWrapperOnCopy } from './components/writing-flow/utils';
import { PrivateRichText } from './components/rich-text/';
Expand Down Expand Up @@ -85,6 +87,7 @@ lock( privateApis, {
usesContextKey,
useFlashEditableBlocks,
globalStylesDataKey,
globalStylesLinksDataKey,
selectBlockPatternsKey,
requiresWrapperOnCopy,
PrivateRichText,
Expand All @@ -95,4 +98,5 @@ lock( privateApis, {
useSpacingSizes,
useBlockDisplayTitle,
__unstableBlockStyleVariationOverridesWithConfig,
setBackgroundStyleDefaults,
} );
1 change: 1 addition & 0 deletions packages/block-editor/src/store/private-keys.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export const globalStylesDataKey = Symbol( 'globalStylesDataKey' );
export const globalStylesLinksDataKey = Symbol( 'globalStylesLinks' );
export const selectBlockPatternsKey = Symbol( 'selectBlockPatternsKey' );
export const reusableBlocksSelectKey = Symbol( 'reusableBlocksSelect' );
22 changes: 22 additions & 0 deletions packages/edit-site/src/components/global-styles/screen-block.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ import {
VariationsPanel,
} from './variations/variations-panel';

// Initial control values where no block style is set.
const BACKGROUND_BLOCK_DEFAULT_VALUES = {
backgroundSize: 'cover',
};

function applyFallbackStyle( border ) {
if ( ! border ) {
return border;
Expand Down Expand Up @@ -70,6 +75,8 @@ const {
useHasFiltersPanel,
useHasImageSettingsPanel,
useGlobalStyle,
useHasBackgroundPanel,
BackgroundPanel: StylesBackgroundPanel,
BorderPanel: StylesBorderPanel,
ColorPanel: StylesColorPanel,
TypographyPanel: StylesTypographyPanel,
Expand Down Expand Up @@ -121,6 +128,7 @@ function ScreenBlock( { name, variation } ) {
}

const blockVariations = useBlockVariations( name );
const hasBackgroundPanel = useHasBackgroundPanel( settings );
const hasTypographyPanel = useHasTypographyPanel( settings );
const hasColorPanel = useHasColorPanel( settings );
const hasBorderPanel = useHasBorderPanel( settings );
Expand Down Expand Up @@ -296,6 +304,20 @@ function ScreenBlock( { name, variation } ) {
/>
) }

{ hasBackgroundPanel && (
<StylesBackgroundPanel
inheritedValue={ inheritedStyle }
value={ style }
onChange={ setStyle }
settings={ settings }
defaultValues={ BACKGROUND_BLOCK_DEFAULT_VALUES }
defaultControls={
blockType?.supports?.background
?.__experimentalDefaultControls
}
/>
) }

{ canEditCSS && (
<PanelBody title={ __( 'Advanced' ) } initialOpen={ false }>
<p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,11 @@ function ScreenLayout() {
const [ rawSettings ] = useGlobalSetting( '' );
const settings = useSettingsForBlockElement( rawSettings );
const hasDimensionsPanel = useHasDimensionsPanel( settings );
const hasBackgroundPanel = useHasBackgroundPanel( settings );
/*
* Use the raw settings to determine if the background panel should be displayed,
* as the background panel is not dependent on the block element settings.
*/
const hasBackgroundPanel = useHasBackgroundPanel( rawSettings );
return (
<>
<ScreenHeader title={ __( 'Layout' ) } />
Expand Down
Loading
Loading