2023-09-11 09:48:23 +00:00
// Reference: https://github.com/WordPress/gutenberg/blob/d5ab7238e53d0947d4bb0853464b1c58325b6130/packages/edit-site/src/components/global-styles/style-variations-container.js
/ * *
* External dependencies
* /
import classnames from 'classnames' ;
import { useMemo , useContext } from '@wordpress/element' ;
import { ENTER } from '@wordpress/keycodes' ;
import { _ _ , sprintf } from '@wordpress/i18n' ;
2023-10-12 04:32:20 +00:00
import {
privateApis as blockEditorPrivateApis ,
BlockEditorProvider ,
} from '@wordpress/block-editor' ;
2023-09-11 09:48:23 +00:00
import { mergeBaseAndUserConfigs } from '@wordpress/edit-site/build-module/components/global-styles/global-styles-provider' ;
import { unlock } from '@wordpress/edit-site/build-module/lock-unlock' ;
2023-10-12 04:32:20 +00:00
import { isEqual , noop } from 'lodash' ;
2023-09-11 09:48:23 +00:00
2023-09-13 08:01:28 +00:00
const { GlobalStylesContext } = unlock ( blockEditorPrivateApis ) ;
2023-09-11 09:48:23 +00:00
2024-02-08 12:38:20 +00:00
// Removes the typography settings from the styles when the user is changing
// to a new typography variation. Otherwise, some of the user's old
// typography settings will persist making new typography settings
// depend on old ones
const resetTypographySettings = ( variation , userStyles ) => {
if ( variation . settings . typography ) {
delete userStyles . typography ;
for ( const elementKey in userStyles . elements ) {
if ( userStyles . elements [ elementKey ] . typography ) {
delete userStyles . elements [ elementKey ] . typography ;
}
}
}
return userStyles ;
} ;
2024-02-20 09:48:27 +00:00
// mergeBaseAndUserConfigs is just a wrapper around deepmerge library: https://github.com/WordPress/gutenberg/blob/237865fad0864c209a7c3e771e23fe66f4fbca25/packages/edit-site/src/components/global-styles/global-styles-provider.js/#L24-L31
// Deepmerge library merges two objects x and y deeply, returning a new merged object with the elements from both x and y.
// In the case of the variation.title === 'New - Neutral', the core/button is an empty object, because we don't want that the classes for the core/button are created.
// Deepmerge merges the userStyles.blocks[ 'core/button' ] with the variation.styles.blocks[ 'core/button' ] and the result is an object with values that doesn't match with the variation. For this reason it is necessary remove the userStyles.blocks[ 'core/button' ].
const resetStyleSettings = ( variation , userStyles ) => {
if ( variation . title === 'New - Neutral' ) {
delete userStyles . blocks [ 'core/button' ] ;
}
return userStyles ;
} ;
2023-09-11 09:48:23 +00:00
export const VariationContainer = ( { variation , children } ) => {
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 = ( ) => {
2023-09-20 04:52:22 +00:00
// Remove the hasCreatedOwnColors flag if the user is switching to a color palette
2023-09-27 08:27:04 +00:00
// hasCreatedOwnColors flag is used for visually deselecting preset color palettes if user has created their own
2023-09-20 04:52:22 +00:00
if (
variation . settings . color &&
user . settings . color &&
2023-09-27 08:27:04 +00:00
user . settings . color . palette . hasCreatedOwnColors
2023-09-20 04:52:22 +00:00
) {
delete user . settings . color . palette . hasCreatedOwnColors ;
2023-09-27 08:27:04 +00:00
// some color palettes don't define all the possible color options, e.g headings and captions
// if the user selects a pre-defined color palette with some own colors defined for these,
// we need to delete these user customizations as the below merge will persist them since
// the incoming variation won't have these properties defined
delete user . styles . color ;
for ( const elementKey in user . styles . elements ) {
if ( user . styles . elements [ elementKey ] . color ) {
delete user . styles . elements [ elementKey ] . color ;
}
}
2023-09-20 04:52:22 +00:00
}
2024-02-20 09:48:27 +00:00
const resetTypographySettingsStyles = resetTypographySettings (
variation ,
user . styles
) ;
const resetStyleSettingsStyles = resetStyleSettings (
variation ,
resetTypographySettingsStyles
) ;
2023-09-11 09:48:23 +00:00
setUserConfig ( ( ) => {
return {
2023-09-13 08:01:28 +00:00
settings : mergeBaseAndUserConfigs (
user . settings ,
variation . settings
) ,
styles : mergeBaseAndUserConfigs (
2024-02-20 09:48:27 +00:00
resetStyleSettingsStyles ,
2023-09-13 08:01:28 +00:00
variation . styles
) ,
2023-09-11 09:48:23 +00:00
} ;
} ) ;
} ;
const selectOnEnter = ( event ) => {
if ( event . keyCode === ENTER ) {
event . preventDefault ( ) ;
selectVariation ( ) ;
}
} ;
const isActive = useMemo ( ( ) => {
2023-09-13 08:01:28 +00:00
if ( variation . settings . color ) {
return isEqual ( variation . settings . color , user . settings . color ) ;
}
2024-02-08 15:08:38 +00:00
// With the Font Library, the fontFamilies object contains an array of font families installed with the Font Library under the key 'custom'.
// We need to compare only the active theme font families, so we compare the theme font families with the current variation.
const { theme } = user . settings . typography . fontFamilies ;
2024-03-07 18:44:01 +00:00
return (
variation . settings . typography ? . fontFamilies . theme . every (
( { slug } ) =>
theme . some ( ( { slug : themeSlug } ) => themeSlug === slug )
) &&
theme . length ===
variation . settings . typography ? . fontFamilies . theme . length
2024-02-20 09:48:09 +00:00
) ;
2023-09-11 09:48:23 +00:00
} , [ user , variation ] ) ;
let label = variation ? . title ;
if ( variation ? . description ) {
label = sprintf (
/* translators: %1$s: variation title. %2$s variation description. */
_ _ ( '%1$s (%2$s)' , 'woocommerce' ) ,
variation ? . title ,
variation ? . description
) ;
}
return (
2023-10-12 04:32:20 +00:00
< BlockEditorProvider
onChange = { noop }
onInput = { noop }
settings = { { } }
useSubRegistry = { true }
>
< GlobalStylesContext.Provider value = { context } >
< div
className = { classnames (
'woocommerce-customize-store_global-styles-variations_item' ,
{
'is-active' : isActive ,
}
) }
role = "button"
onClick = { selectVariation }
onKeyDown = { selectOnEnter }
tabIndex = "0"
aria - label = { label }
aria - current = { isActive }
>
< div className = "woocommerce-customize-store_global-styles-variations_item-preview" >
{ children }
< / div >
2023-09-11 09:48:23 +00:00
< / div >
2023-10-12 04:32:20 +00:00
< / GlobalStylesContext.Provider >
< / BlockEditorProvider >
2023-09-11 09:48:23 +00:00
) ;
} ;