diff --git a/packages/edit-site/src/components/global-styles/color-palette-panel.js b/packages/edit-site/src/components/global-styles/color-palette-panel.js index 03401cab9f80b..9e6bb5dbe2c77 100644 --- a/packages/edit-site/src/components/global-styles/color-palette-panel.js +++ b/packages/edit-site/src/components/global-styles/color-palette-panel.js @@ -13,6 +13,8 @@ import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor'; * Internal dependencies */ import { unlock } from '../../lock-unlock'; +import ColorVariations from './variations-color'; +import { useCurrentMergeThemeStyleVariationsWithUserConfig } from '../../hooks/use-theme-style-variations/use-theme-style-variations-by-property'; const { useGlobalSetting } = unlock( blockEditorPrivateApis ); const mobilePopoverProps = { placement: 'bottom-start', offset: 8 }; @@ -45,7 +47,12 @@ export default function ColorPalettePanel( { name } ) { 'color.defaultPalette', name ); - + const colorVariations = useCurrentMergeThemeStyleVariationsWithUserConfig( { + property: 'color', + filter: ( variation ) => + variation?.settings?.color && + Object.keys( variation?.settings?.color ).length, + } ); const isMobileViewport = useViewportMatch( 'small', '<' ); const popoverProps = isMobileViewport ? mobilePopoverProps : undefined; @@ -78,6 +85,9 @@ export default function ColorPalettePanel( { name } ) { popoverProps={ popoverProps } /> ) } + { !! colorVariations.length && ( + + ) } face.fontStyle.toLowerCase() === 'normal' + ( face ) => + face?.fontStyle && face.fontStyle.toLowerCase() === 'normal' ); if ( normalFaces.length > 0 ) { style.fontStyle = 'normal'; diff --git a/packages/edit-site/src/components/global-styles/preview-colors.js b/packages/edit-site/src/components/global-styles/preview-colors.js new file mode 100644 index 0000000000000..88885baf30062 --- /dev/null +++ b/packages/edit-site/src/components/global-styles/preview-colors.js @@ -0,0 +1,216 @@ +/** + * WordPress dependencies + */ +import { + __unstableIframe as Iframe, + __unstableEditorStyles as EditorStyles, + privateApis as blockEditorPrivateApis, +} from '@wordpress/block-editor'; +import { + __unstableMotion as motion, + __experimentalHStack as HStack, +} from '@wordpress/components'; +import { + useThrottle, + useReducedMotion, + useResizeObserver, +} from '@wordpress/compose'; +import { useLayoutEffect, useState, useMemo } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import { unlock } from '../../lock-unlock'; +import { useStylesPreviewColors } from './hooks'; + +const { useGlobalStyle, useGlobalStylesOutput } = unlock( + blockEditorPrivateApis +); + +const firstFrame = { + start: { + scale: 1, + opacity: 1, + }, + hover: { + scale: 0, + opacity: 0, + }, +}; + +const normalizedWidth = 248; +const normalizedHeight = 152; + +const normalizedColorSwatchSize = 66; + +// Throttle options for useThrottle. Must be defined outside of the component, +// so that the object reference is the same on each render. +const THROTTLE_OPTIONS = { + leading: true, + trailing: true, +}; + +const StylesPreviewColors = ( { label, isFocused, withHoverView } ) => { + const [ backgroundColor = 'white' ] = useGlobalStyle( 'color.background' ); + const [ gradientValue ] = useGlobalStyle( 'color.gradient' ); + const [ styles ] = useGlobalStylesOutput(); + const disableMotion = useReducedMotion(); + const [ isHovered, setIsHovered ] = useState( false ); + const [ containerResizeListener, { width } ] = useResizeObserver(); + const [ throttledWidth, setThrottledWidthState ] = useState( width ); + const [ ratioState, setRatioState ] = useState(); + + const setThrottledWidth = useThrottle( + setThrottledWidthState, + 250, + THROTTLE_OPTIONS + ); + + // Must use useLayoutEffect to avoid a flash of the iframe at the wrong + // size before the width is set. + useLayoutEffect( () => { + if ( width ) { + setThrottledWidth( width ); + } + }, [ width, setThrottledWidth ] ); + + // Must use useLayoutEffect to avoid a flash of the iframe at the wrong + // size before the width is set. + useLayoutEffect( () => { + const newRatio = throttledWidth ? throttledWidth / normalizedWidth : 1; + const ratioDiff = newRatio - ( ratioState || 0 ); + + // Only update the ratio state if the difference is big enough + // or if the ratio state is not yet set. This is to avoid an + // endless loop of updates at particular viewport heights when the + // presence of a scrollbar causes the width to change slightly. + const isRatioDiffBigEnough = Math.abs( ratioDiff ) > 0.1; + + if ( isRatioDiffBigEnough || ! ratioState ) { + setRatioState( newRatio ); + } + }, [ throttledWidth, ratioState ] ); + + // Set a fallbackRatio to use before the throttled ratio has been set. + const fallbackRatio = width ? width / normalizedWidth : 1; + /* + * Use the throttled ratio if it has been calculated, otherwise + * use the fallback ratio. The throttled ratio is used to avoid + * an endless loop of updates at particular viewport heights. + * See: https://github.com/WordPress/gutenberg/issues/55112 + */ + const ratio = ratioState ? ratioState : fallbackRatio; + + const { highlightedColors } = useStylesPreviewColors(); + + /* + * Reset leaked styles from WP common.css and remove main content layout padding and border. + * Add pointer cursor to the body to indicate the iframe is interactive, + * similar to Typography variation previews. + */ + const editorStyles = useMemo( () => { + if ( styles ) { + return [ + ...styles, + { + css: 'html{overflow:hidden}body{min-width: 0;padding: 0;border: none;cursor: pointer;}', + isGlobalStyles: true, + }, + ]; + } + + return styles; + }, [ styles ] ); + const isReady = !! width; + + return ( + <> +
+ { containerResizeListener } +
+ { isReady && ( + + ) } + + ); +}; + +export default StylesPreviewColors; diff --git a/packages/edit-site/src/components/global-styles/screen-colors.js b/packages/edit-site/src/components/global-styles/screen-colors.js index a19bd16c8839f..c9ddef9c6230d 100644 --- a/packages/edit-site/src/components/global-styles/screen-colors.js +++ b/packages/edit-site/src/components/global-styles/screen-colors.js @@ -11,6 +11,8 @@ import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor'; import ScreenHeader from './header'; import Palette from './palette'; import { unlock } from '../../lock-unlock'; +import ColorVariations from './variations-color'; +import { useCurrentMergeThemeStyleVariationsWithUserConfig } from '../../hooks/use-theme-style-variations/use-theme-style-variations-by-property'; const { useGlobalStyle, @@ -29,6 +31,12 @@ function ScreenColors() { const [ rawSettings ] = useGlobalSetting( '' ); const settings = useSettingsForBlockElement( rawSettings ); + const colorVariations = useCurrentMergeThemeStyleVariationsWithUserConfig( { + property: 'color', + filter: ( variation ) => + variation?.settings?.color && + Object.keys( variation?.settings?.color ).length, + } ); return ( <>
- + + { !! colorVariations.length && ( + + ) } + + variation?.settings?.typography?.fontFamilies && + Object.keys( variation?.settings?.typography?.fontFamilies ) + .length, + } ); return ( <> @@ -30,7 +40,14 @@ function ScreenTypography() { />
- { fontLibraryEnabled && } + { !! typographyVariations.length && ( + + ) } + { ! window.__experimentalDisableFontLibrary && ( + + { fontLibraryEnabled && } + + ) }
diff --git a/packages/edit-site/src/components/global-styles/style-variations-container.js b/packages/edit-site/src/components/global-styles/style-variations-container.js index 6cc8b53b800d3..3ca856dd8fb90 100644 --- a/packages/edit-site/src/components/global-styles/style-variations-container.js +++ b/packages/edit-site/src/components/global-styles/style-variations-container.js @@ -1,104 +1,17 @@ -/** - * External dependencies - */ -import classnames from 'classnames'; - /** * WordPress dependencies */ import { store as coreStore } from '@wordpress/core-data'; import { useSelect } from '@wordpress/data'; -import { useMemo, useContext, useState } from '@wordpress/element'; -import { ENTER } from '@wordpress/keycodes'; +import { useMemo } from '@wordpress/element'; import { __experimentalGrid as Grid } from '@wordpress/components'; -import { __, sprintf } from '@wordpress/i18n'; -import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor'; +import { __ } from '@wordpress/i18n'; /** * Internal dependencies */ -import { mergeBaseAndUserConfigs } from './global-styles-provider'; import StylesPreview from './preview'; -import { unlock } from '../../lock-unlock'; - -const { GlobalStylesContext, areGlobalStyleConfigsEqual } = unlock( - blockEditorPrivateApis -); - -function Variation( { variation } ) { - const [ isFocused, setIsFocused ] = useState( false ); - const { base, user, setUserConfig } = useContext( GlobalStylesContext ); - const context = useMemo( () => { - return { - user: { - settings: variation.settings ?? {}, - styles: variation.styles ?? {}, - }, - base, - merged: mergeBaseAndUserConfigs( base, variation ), - setUserConfig: () => {}, - }; - }, [ variation, base ] ); - - const selectVariation = () => { - setUserConfig( () => { - return { - settings: variation.settings, - styles: variation.styles, - }; - } ); - }; - - const selectOnEnter = ( event ) => { - if ( event.keyCode === ENTER ) { - event.preventDefault(); - selectVariation(); - } - }; - - const isActive = useMemo( () => { - return areGlobalStyleConfigsEqual( user, variation ); - }, [ user, variation ] ); - - let label = variation?.title; - if ( variation?.description ) { - label = sprintf( - /* translators: %1$s: variation title. %2$s variation description. */ - __( '%1$s (%2$s)' ), - variation?.title, - variation?.description - ); - } - - return ( - -
setIsFocused( true ) } - onBlur={ () => setIsFocused( false ) } - > -
- -
-
-
- ); -} +import Variation from './variation'; export default function StyleVariationsContainer() { const variations = useSelect( ( select ) => { @@ -128,7 +41,15 @@ export default function StyleVariationsContainer() { className="edit-site-global-styles-style-variations-container" > { withEmptyVariation.map( ( variation, index ) => ( - + + { ( isFocused ) => ( + + ) } + ) ) } ); diff --git a/packages/edit-site/src/components/global-styles/style.scss b/packages/edit-site/src/components/global-styles/style.scss index f53173437d47d..d1e0a49384705 100644 --- a/packages/edit-site/src/components/global-styles/style.scss +++ b/packages/edit-site/src/components/global-styles/style.scss @@ -95,6 +95,7 @@ box-sizing: border-box; // To round the outline in Windows 10 high contrast mode. border-radius: $radius-block-ui; + cursor: pointer; .edit-site-global-styles-variations_item-preview { padding: $border-width * 2; @@ -102,6 +103,10 @@ box-shadow: 0 0 0 $border-width $gray-200; // Shown in Windows 10 high contrast mode. outline: 1px solid transparent; + + .edit-site-global-styles-color-variations & { + padding: $grid-unit-10; + } } &.is-active .edit-site-global-styles-variations_item-preview { @@ -199,3 +204,9 @@ .edit-site-global-styles-sidebar__panel .block-editor-block-icon svg { fill: currentColor; } + +.edit-site-global-styles-type-preview { + font-size: 22px; + line-height: 44px; + text-align: center; +} diff --git a/packages/edit-site/src/components/global-styles/variation.js b/packages/edit-site/src/components/global-styles/variation.js new file mode 100644 index 0000000000000..cc669130ea516 --- /dev/null +++ b/packages/edit-site/src/components/global-styles/variation.js @@ -0,0 +1,93 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; + +/** + * WordPress dependencies + */ +import { useMemo, useContext, useState } from '@wordpress/element'; +import { ENTER } from '@wordpress/keycodes'; +import { __, sprintf } from '@wordpress/i18n'; +import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor'; + +/** + * Internal dependencies + */ +import { mergeBaseAndUserConfigs } from './global-styles-provider'; +import { unlock } from '../../lock-unlock'; + +const { GlobalStylesContext, areGlobalStyleConfigsEqual } = unlock( + blockEditorPrivateApis +); + +export default function Variation( { variation, children } ) { + const [ isFocused, setIsFocused ] = useState( false ); + const { base, user, setUserConfig } = useContext( GlobalStylesContext ); + const context = useMemo( + () => ( { + user: { + settings: variation.settings ?? {}, + styles: variation.styles ?? {}, + }, + base, + merged: mergeBaseAndUserConfigs( base, variation ), + setUserConfig: () => {}, + } ), + [ variation, base ] + ); + + const selectVariation = () => { + setUserConfig( () => ( { + settings: variation.settings, + styles: variation.styles, + } ) ); + }; + + const selectOnEnter = ( event ) => { + if ( event.keyCode === ENTER ) { + event.preventDefault(); + selectVariation(); + } + }; + + const isActive = useMemo( + () => areGlobalStyleConfigsEqual( user, variation ), + [ user, variation ] + ); + + let label = variation?.title; + if ( variation?.description ) { + label = sprintf( + /* translators: %1$s: variation title. %2$s variation description. */ + __( '%1$s (%2$s)' ), + variation?.title, + variation?.description + ); + } + + return ( + +
setIsFocused( true ) } + onBlur={ () => setIsFocused( false ) } + > +
+ { children( isFocused ) } +
+
+
+ ); +} diff --git a/packages/edit-site/src/components/global-styles/variations-color.js b/packages/edit-site/src/components/global-styles/variations-color.js new file mode 100644 index 0000000000000..badb07336d303 --- /dev/null +++ b/packages/edit-site/src/components/global-styles/variations-color.js @@ -0,0 +1,30 @@ +/** + * WordPress dependencies + */ +import { + __experimentalGrid as Grid, + __experimentalVStack as VStack, +} from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import Subtitle from './subtitle'; +import Variation from './variation'; +import StylesPreviewColors from './preview-colors'; + +export default function ColorVariations( { variations } ) { + return ( + + { __( 'Presets' ) } + + { variations.map( ( variation, index ) => ( + + { () => } + + ) ) } + + + ); +} diff --git a/packages/edit-site/src/components/global-styles/variations-typography.js b/packages/edit-site/src/components/global-styles/variations-typography.js new file mode 100644 index 0000000000000..cd30ed0b07756 --- /dev/null +++ b/packages/edit-site/src/components/global-styles/variations-typography.js @@ -0,0 +1,162 @@ +/** + * WordPress dependencies + */ +import { useContext } from '@wordpress/element'; +import { + __experimentalGrid as Grid, + __experimentalVStack as VStack, + __unstableMotion as motion, +} from '@wordpress/components'; +import { __, _x } from '@wordpress/i18n'; +import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor'; + +/** + * Internal dependencies + */ +import { mergeBaseAndUserConfigs } from './global-styles-provider'; +import { unlock } from '../../lock-unlock'; +import { getFamilyPreviewStyle } from './font-library-modal/utils/preview-styles'; +import { useCurrentMergeThemeStyleVariationsWithUserConfig } from '../../hooks/use-theme-style-variations/use-theme-style-variations-by-property'; +import Subtitle from './subtitle'; +import Variation from './variation'; + +const { GlobalStylesContext } = unlock( blockEditorPrivateApis ); + +function getFontFamilyFromSetting( fontFamilies, setting ) { + if ( ! setting ) { + return null; + } + + const fontFamilyVariable = setting.replace( 'var(', '' ).replace( ')', '' ); + const fontFamilySlug = fontFamilyVariable?.split( '--' ).slice( -1 )[ 0 ]; + + return fontFamilies.find( + ( fontFamily ) => fontFamily.slug === fontFamilySlug + ); +} + +const getFontFamilies = ( themeJson ) => { + const fontFamilies = themeJson?.settings?.typography?.fontFamilies?.theme; // TODO this could not be under theme. + const bodyFontFamilySetting = themeJson?.styles?.typography?.fontFamily; + const bodyFontFamily = getFontFamilyFromSetting( + fontFamilies, + bodyFontFamilySetting + ); + + const headingFontFamilySetting = + themeJson?.styles?.elements?.heading?.typography?.fontFamily; + + let headingFontFamily; + if ( ! headingFontFamilySetting ) { + headingFontFamily = bodyFontFamily; + } else { + headingFontFamily = getFontFamilyFromSetting( + fontFamilies, + themeJson?.styles?.elements?.heading?.typography?.fontFamily + ); + } + + return [ bodyFontFamily, headingFontFamily ]; +}; + +const TypePreview = ( { variation } ) => { + const { base } = useContext( GlobalStylesContext ); + const [ bodyFontFamilies, headingFontFamilies ] = getFontFamilies( + mergeBaseAndUserConfigs( base, variation ) + ); + const bodyPreviewStyle = bodyFontFamilies + ? getFamilyPreviewStyle( bodyFontFamilies ) + : {}; + const headingPreviewStyle = headingFontFamilies + ? getFamilyPreviewStyle( headingFontFamilies ) + : {}; + return ( + + + { _x( 'A', 'Uppercase letter A' ) } + + + { _x( 'a', 'Lowercase letter A' ) } + + + ); +}; + +export default function TypographyVariations() { + const typographyVariations = + useCurrentMergeThemeStyleVariationsWithUserConfig( { + property: 'typography', + filter: ( variation ) => + variation?.settings?.typography?.fontFamilies && + Object.keys( variation?.settings?.typography?.fontFamilies ) + .length, + } ); + + const { base } = useContext( GlobalStylesContext ); + + /* + * Filter duplicate variations based on the font families used in the variation. + */ + const uniqueTypographyVariations = typographyVariations?.length + ? Object.values( + typographyVariations.reduce( ( acc, variation ) => { + const [ bodyFontFamily, headingFontFamily ] = + getFontFamilies( + mergeBaseAndUserConfigs( base, variation ) + ); + if ( + headingFontFamily?.name && + bodyFontFamily?.name && + ! acc[ + `${ headingFontFamily?.name }:${ bodyFontFamily?.name }` + ] + ) { + acc[ + `${ headingFontFamily?.name }:${ bodyFontFamily?.name }` + ] = variation; + } + + return acc; + }, {} ) + ) + : []; + + return ( + + { __( 'Presets' ) } + + { typographyVariations && typographyVariations.length + ? uniqueTypographyVariations.map( ( variation, index ) => { + return ( + + { () => ( + + ) } + + ); + } ) + : null } + + + ); +} diff --git a/packages/edit-site/src/hooks/use-theme-style-variations/test/use-theme-style-variations-by-property.js b/packages/edit-site/src/hooks/use-theme-style-variations/test/use-theme-style-variations-by-property.js index ced849f8fb121..23db164b24895 100644 --- a/packages/edit-site/src/hooks/use-theme-style-variations/test/use-theme-style-variations-by-property.js +++ b/packages/edit-site/src/hooks/use-theme-style-variations/test/use-theme-style-variations-by-property.js @@ -8,6 +8,7 @@ import { renderHook } from '@testing-library/react'; */ import useThemeStyleVariationsByProperty, { filterObjectByProperty, + removePropertyFromObject, } from '../use-theme-style-variations-by-property'; describe( 'filterObjectByProperty', () => { @@ -962,3 +963,175 @@ describe( 'useThemeStyleVariationsByProperty', () => { ] ); } ); } ); + +describe( 'removePropertyFromObject', () => { + const mockBaseVariation = { + settings: { + typography: { + fontFamilies: { + custom: [ + { + name: 'ADLaM Display', + fontFamily: 'ADLaM Display, system-ui', + slug: 'adlam-display', + fontFace: [ + { + src: 'adlam.woff2', + fontWeight: '400', + fontStyle: 'normal', + fontFamily: 'ADLaM Display', + }, + ], + }, + ], + }, + fontSizes: [ + { + name: 'Base small', + slug: 'base-small', + size: '1px', + }, + { + name: 'Base medium', + slug: 'base-medium', + size: '2px', + }, + { + name: 'Base large', + slug: 'base-large', + size: '3px', + }, + ], + }, + color: { + palette: { + custom: [ + { + color: '#c42727', + name: 'Color 1', + slug: 'custom-color-1', + }, + { + color: '#3b0f0f', + name: 'Color 2', + slug: 'custom-color-2', + }, + ], + }, + }, + layout: { + wideSize: '1137px', + contentSize: '400px', + }, + }, + styles: { + typography: { + fontSize: '12px', + lineHeight: '1.5', + }, + color: { + backgroundColor: 'cheese', + color: 'lettuce', + }, + elements: { + cite: { + color: { + text: 'white', + }, + typography: { + letterSpacing: 'white', + }, + }, + }, + blocks: { + 'core/quote': { + color: { + text: 'hello', + background: 'dolly', + }, + typography: { + fontSize: '111111px', + }, + }, + 'core/group': { + typography: { + fontFamily: 'var:preset|font-family|system-sans-serif', + }, + }, + }, + }, + }; + + it( 'should return with no property', () => { + const object = { test: 'me' }; + expect( removePropertyFromObject( object, undefined ) ).toEqual( + object + ); + } ); + + it( 'should return with non-string property', () => { + const object = { test: 'you' }; + expect( removePropertyFromObject( object, true ) ).toEqual( object ); + } ); + + it( 'should return with empty object', () => { + const object = {}; + expect( removePropertyFromObject( object, 'color' ) ).toEqual( object ); + } ); + + it( 'should remove the specified property from the object', () => { + expect( + removePropertyFromObject( + { + ...mockBaseVariation, + }, + 'typography' + ) + ).toEqual( { + settings: { + color: { + palette: { + custom: [ + { + color: '#c42727', + name: 'Color 1', + slug: 'custom-color-1', + }, + { + color: '#3b0f0f', + name: 'Color 2', + slug: 'custom-color-2', + }, + ], + }, + }, + layout: { + wideSize: '1137px', + contentSize: '400px', + }, + }, + styles: { + color: { + backgroundColor: 'cheese', + color: 'lettuce', + }, + elements: { + cite: { + color: { + text: 'white', + }, + }, + }, + blocks: { + 'core/quote': { + color: { + text: 'hello', + background: 'dolly', + }, + }, + 'core/group': {}, + }, + }, + } ); + } ); +} ); diff --git a/packages/edit-site/src/hooks/use-theme-style-variations/use-theme-style-variations-by-property.js b/packages/edit-site/src/hooks/use-theme-style-variations/use-theme-style-variations-by-property.js index b9c1b40ec7c1d..d15b624c9eb44 100644 --- a/packages/edit-site/src/hooks/use-theme-style-variations/use-theme-style-variations-by-property.js +++ b/packages/edit-site/src/hooks/use-theme-style-variations/use-theme-style-variations-by-property.js @@ -1,13 +1,76 @@ /** * WordPress dependencies */ -import { useMemo } from '@wordpress/element'; +import { useSelect } from '@wordpress/data'; +import { store as coreStore } from '@wordpress/core-data'; +import { useContext, useMemo } from '@wordpress/element'; +import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor'; /** * Internal dependencies */ import { mergeBaseAndUserConfigs } from '../../components/global-styles/global-styles-provider'; import cloneDeep from '../../utils/clone-deep'; +import { unlock } from '../../lock-unlock'; + +const { GlobalStylesContext } = unlock( blockEditorPrivateApis ); + +/** + * Removes all instances of a property from an object. + * + * @param {Object} object The object to remove the property from. + * @param {string} property The property to remove. + * @return {Object} The modified object. + */ +export function removePropertyFromObject( object, property ) { + if ( ! property || typeof property !== 'string' ) { + return object; + } + + if ( typeof object !== 'object' || ! Object.keys( object ).length ) { + return object; + } + + for ( const key in object ) { + if ( key === property ) { + delete object[ key ]; + } else if ( typeof object[ key ] === 'object' ) { + removePropertyFromObject( object[ key ], property ); + } + } + return object; +} + +/** + * A convenience wrapper for `useThemeStyleVariationsByProperty()` that fetches the current theme style variations, + * and user-defined global style/settings object. + * + * @param {Object} props Object of hook args. + * @param {string} props.property The property to filter by. + * @param {Function} props.filter Optional. The filter function to apply to the variations. + * @return {Object[]|*} The merged object. + */ +export function useCurrentMergeThemeStyleVariationsWithUserConfig( { + property, + filter, +} ) { + const variations = useSelect( ( select ) => { + return select( + coreStore + ).__experimentalGetCurrentThemeGlobalStylesVariations(); + }, [] ); + const { user: baseVariation } = useContext( GlobalStylesContext ); + + return useThemeStyleVariationsByProperty( { + variations, + property, + filter, + baseVariation: removePropertyFromObject( + cloneDeep( baseVariation ), + property + ), + } ); +} /** * Returns a new object, with properties specified in `property`, @@ -40,6 +103,10 @@ export const filterObjectByProperty = ( object, property ) => { /** * Returns a new object with only the properties specified in `property`. + * Optional merges the baseVariation object with the variation object. + * Note: this function will only overwrite the specified property in baseVariation if it exists. + * The baseVariation will not be otherwise modified. To strip a property from the baseVariation object, use `removePropertyFromObject`. + * See useCurrentMergeThemeStyleVariationsWithUserConfig for an example of how to use this function. * * @param {Object} props Object of hook args. * @param {Object[]} props.variations The theme style variations to filter.