diff --git a/packages/block-editor/src/hooks/background.js b/packages/block-editor/src/hooks/background.js index d093d3da55c8d6..9524564b487ecb 100644 --- a/packages/block-editor/src/hooks/background.js +++ b/packages/block-editor/src/hooks/background.js @@ -18,6 +18,7 @@ import { __experimentalVStack as VStack, DropZone, FlexItem, + FocalPointPicker, MenuItem, VisuallyHidden, __experimentalItemGroup as ItemGroup, @@ -59,13 +60,17 @@ export function hasBackgroundImageValue( style ) { /** * Checks if there is a current value in the background size block support - * attributes. + * attributes. Background size values include background size as well + * as background position. * * @param {Object} style Style attribute. * @return {boolean} Whether or not the block has a background size value set. */ export function hasBackgroundSizeValue( style ) { - return style?.background?.backgroundSize !== undefined; + return ( + style?.background?.backgroundPosition !== undefined || + style?.background?.backgroundSize !== undefined + ); } /** @@ -130,6 +135,7 @@ function resetBackgroundSize( style = {}, setAttributes ) { ...style, background: { ...style?.background, + backgroundPosition: undefined, backgroundRepeat: undefined, backgroundSize: undefined, }, @@ -367,6 +373,26 @@ function backgroundSizeHelpText( value ) { return __( 'Set a fixed width.' ); } +export const coordsToBackgroundPosition = ( value ) => { + if ( ! value || isNaN( value.x ) || isNaN( value.y ) ) { + return undefined; + } + + return `${ value.x * 100 }% ${ value.y * 100 }%`; +}; + +export const backgroundPositionToCoords = ( value ) => { + if ( ! value ) { + return { x: undefined, y: undefined }; + } + + let [ x, y ] = value.split( ' ' ).map( ( v ) => parseFloat( v ) / 100 ); + x = isNaN( x ) ? undefined : x; + y = isNaN( y ) ? x : y; + + return { x, y }; +}; + function BackgroundSizePanelItem( { clientId, isShownByDefault, @@ -446,6 +472,18 @@ function BackgroundSizePanelItem( { } ); }; + const updateBackgroundPosition = ( next ) => { + setAttributes( { + style: cleanEmptyObject( { + ...style, + background: { + ...style?.background, + backgroundPosition: coordsToBackgroundPosition( next ), + }, + } ), + } ); + }; + const toggleIsRepeated = () => { setAttributes( { style: cleanEmptyObject( { @@ -471,6 +509,16 @@ function BackgroundSizePanelItem( { resetAllFilter={ resetAllFilter } panelId={ clientId } > + { + it( 'should return the correct coordinates for a percentage value using 2-value syntax', () => { + expect( backgroundPositionToCoords( '25% 75%' ) ).toEqual( { + x: 0.25, + y: 0.75, + } ); + } ); + + it( 'should return the correct coordinates for a percentage using 1-value syntax', () => { + expect( backgroundPositionToCoords( '50%' ) ).toEqual( { + x: 0.5, + y: 0.5, + } ); + } ); + + it( 'should return undefined coords in given an empty value', () => { + expect( backgroundPositionToCoords( '' ) ).toEqual( { + x: undefined, + y: undefined, + } ); + } ); + + it( 'should return undefined coords in given a string that cannot be converted', () => { + expect( backgroundPositionToCoords( 'apples' ) ).toEqual( { + x: undefined, + y: undefined, + } ); + } ); +} ); + +describe( 'coordsToBackgroundPosition', () => { + it( 'should return the correct background position for a set of coordinates', () => { + expect( coordsToBackgroundPosition( { x: 0.25, y: 0.75 } ) ).toBe( + '25% 75%' + ); + } ); + + it( 'should return undefined if no coordinates are provided', () => { + expect( coordsToBackgroundPosition( {} ) ).toBeUndefined(); + } ); +} ); diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index 94f69636b3e053..3c4dd1e29476c5 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -9,6 +9,7 @@ ### Bug Fix +- `FocalPointPicker`: Allow `PointerCircle` to render in a default centered position when x and y coordinates are undefined ([#58592](https://github.com/WordPress/gutenberg/pull/58592)). - `DateTime`: Add a timezone offset value for display purposes. ([#56682](https://github.com/WordPress/gutenberg/pull/56682)). - `Placeholder`: Fix Placeholder component padding when body text font size is changed ([#58323](https://github.com/WordPress/gutenberg/pull/58323)). - `Placeholder`: Fix Global Styles typography settings bleeding into placeholder component ([#58303](https://github.com/WordPress/gutenberg/pull/58303)). diff --git a/packages/components/src/focal-point-picker/index.tsx b/packages/components/src/focal-point-picker/index.tsx index 1b4c4d9dff9665..33fd505f67fe17 100644 --- a/packages/components/src/focal-point-picker/index.tsx +++ b/packages/components/src/focal-point-picker/index.tsx @@ -217,8 +217,8 @@ export function FocalPointPicker( { }; const focalPointPosition = { - left: x * bounds.width, - top: y * bounds.height, + left: x !== undefined ? x * bounds.width : 0.5 * bounds.width, + top: y !== undefined ? y * bounds.height : 0.5 * bounds.height, }; const classes = classnames( diff --git a/packages/style-engine/src/styles/background/index.ts b/packages/style-engine/src/styles/background/index.ts index b9879ad2032a17..8ce8c7d577fb28 100644 --- a/packages/style-engine/src/styles/background/index.ts +++ b/packages/style-engine/src/styles/background/index.ts @@ -40,6 +40,18 @@ const backgroundImage = { }, }; +const backgroundPosition = { + name: 'backgroundRepeat', + generate: ( style: Style, options: StyleOptions ) => { + return generateRule( + style, + options, + [ 'background', 'backgroundPosition' ], + 'backgroundPosition' + ); + }, +}; + const backgroundRepeat = { name: 'backgroundRepeat', generate: ( style: Style, options: StyleOptions ) => { @@ -89,4 +101,9 @@ const backgroundSize = { }, }; -export default [ backgroundImage, backgroundRepeat, backgroundSize ]; +export default [ + backgroundImage, + backgroundPosition, + backgroundRepeat, + backgroundSize, +]; diff --git a/packages/style-engine/src/test/index.js b/packages/style-engine/src/test/index.js index 1727ed535897bc..b679775d3f37f4 100644 --- a/packages/style-engine/src/test/index.js +++ b/packages/style-engine/src/test/index.js @@ -229,6 +229,7 @@ describe( 'getCSSRules', () => { source: 'file', url: 'https://example.com/image.jpg', }, + backgroundPosition: '50% 50%', backgroundRepeat: 'no-repeat', backgroundSize: '300px', }, @@ -384,6 +385,11 @@ describe( 'getCSSRules', () => { key: 'backgroundImage', value: "url( 'https://example.com/image.jpg' )", }, + { + selector: '.some-selector', + key: 'backgroundPosition', + value: '50% 50%', + }, { selector: '.some-selector', key: 'backgroundRepeat',