From 0da4136e439707961b97e25472e7e02e4442ed5d Mon Sep 17 00:00:00 2001 From: ramon Date: Thu, 8 Feb 2024 15:51:58 +1100 Subject: [PATCH] First commit Pulling out the utils from https://github.com/WordPress/gutenberg/pull/56622 so we test separately --- .../use-theme-style-variations-by-property.js | 125 ++++++++++++++++++ .../push-changes-to-global-styles/index.js | 5 +- packages/edit-site/src/utils/clone-deep.js | 8 ++ 3 files changed, 134 insertions(+), 4 deletions(-) create mode 100644 packages/edit-site/src/components/global-styles/use-theme-style-variations-by-property.js create mode 100644 packages/edit-site/src/utils/clone-deep.js diff --git a/packages/edit-site/src/components/global-styles/use-theme-style-variations-by-property.js b/packages/edit-site/src/components/global-styles/use-theme-style-variations-by-property.js new file mode 100644 index 0000000000000..f7305561680c1 --- /dev/null +++ b/packages/edit-site/src/components/global-styles/use-theme-style-variations-by-property.js @@ -0,0 +1,125 @@ +/** + * WordPress dependencies + */ +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 { unlock } from '../../lock-unlock'; +import cloneDeep from '../../utils/clone-deep'; +import { mergeBaseAndUserConfigs } from './global-styles-provider'; +/** + * Returns a new object with only the properties specified in `properties`. + * + * @param {Object} object The object to filter + * @param {Object} property The property to filter by + * @return {Object} The merged object. + */ +export const filterObjectByProperty = ( object, property ) => { + if ( ! object ) { + return {}; + } + + const newObject = {}; + Object.keys( object ).forEach( ( key ) => { + if ( key === property ) { + newObject[ key ] = object[ key ]; + } else if ( typeof object[ key ] === 'object' ) { + const newFilter = filterObjectByProperty( object[ key ], property ); + if ( Object.keys( newFilter ).length ) { + newObject[ key ] = newFilter; + } + } + } ); + return newObject; +}; + +/** + * Removes all instances of a property from an object. + * + * @param {Object} object + * @param {string} property + * @return {Object} The modified object. + */ +const removePropertyFromObject = ( object, property ) => { + for ( const key in object ) { + if ( key === property ) { + delete object[ key ]; + } else if ( typeof object[ key ] === 'object' ) { + removePropertyFromObject( object[ key ], property ); + } + } + return object; +}; + +/** + * Return style variations with all properties removed except for the one specified in `type`. + * + * @param {Object} user The user variation. + * @param {Array} variations The other style variations. + * @param {string} property The property to filter by. + * + * @return {Array} The style variation with only the specified property filtered. + */ +export const getVariationsByProperty = ( user, variations, property ) => { + const userSettingsWithoutProperty = removePropertyFromObject( + cloneDeep( user ), + property + ); + + const variationsWithOnlyProperty = variations.map( ( variation ) => { + return { + ...filterObjectByProperty( variation, property ), + // Add variation title and description to every variation item. + title: variation?.title, + description: variation?.description, + }; + } ); + + return variationsWithOnlyProperty.map( ( variation ) => + mergeBaseAndUserConfigs( userSettingsWithoutProperty, variation ) + ); +}; + +const { GlobalStylesContext } = unlock( blockEditorPrivateApis ); + +export default function useThemeStyleVariationsByProperty( { + styleProperty, + filter, +} ) { + const variations = useSelect( ( select ) => { + return select( + coreStore + ).__experimentalGetCurrentThemeGlobalStylesVariations(); + }, [] ); + const { user } = useContext( GlobalStylesContext ); + + return useMemo( () => { + if ( ! styleProperty || ! variations.length ) { + return []; + } + /* + @TODO: + For colors, should also get filter? + Memoize/cache all this better? E.g., should we memoize the variations? + Test with empty theme + Test with 2022 - typography font families bork for some reason + + */ + let styleVariations = getVariationsByProperty( + user, + variations, + styleProperty + ); + + if ( 'function' === typeof filter ) { + styleVariations = styleVariations.filter( filter ); + } + + return styleVariations; + }, [ styleProperty, variations, filter ] ); +} diff --git a/packages/edit-site/src/hooks/push-changes-to-global-styles/index.js b/packages/edit-site/src/hooks/push-changes-to-global-styles/index.js index 4844ec6d03eb5..21f19201b7510 100644 --- a/packages/edit-site/src/hooks/push-changes-to-global-styles/index.js +++ b/packages/edit-site/src/hooks/push-changes-to-global-styles/index.js @@ -26,6 +26,7 @@ import { store as coreStore } from '@wordpress/core-data'; */ import { useSupportedStyles } from '../../components/global-styles/hooks'; import { unlock } from '../../lock-unlock'; +import cloneDeep from '../../utils/clone-deep'; const { cleanEmptyObject, GlobalStylesContext } = unlock( blockEditorPrivateApis @@ -275,10 +276,6 @@ function setNestedValue( object, path, value ) { return object; } -function cloneDeep( object ) { - return ! object ? {} : JSON.parse( JSON.stringify( object ) ); -} - function PushChangesToGlobalStylesControl( { name, attributes, diff --git a/packages/edit-site/src/utils/clone-deep.js b/packages/edit-site/src/utils/clone-deep.js new file mode 100644 index 0000000000000..149e1df2408ea --- /dev/null +++ b/packages/edit-site/src/utils/clone-deep.js @@ -0,0 +1,8 @@ +/** + * Makes a copy of an object without storing any references to the original object. + * @param {Object} object + * @return {Object} The cloned object. + */ +export default function cloneDeep( object ) { + return ! object ? {} : JSON.parse( JSON.stringify( object ) ); +}