woocommerce/plugins/woocommerce-admin/client/customize-store/assembler-hub/sidebar/global-styles/variation-container.jsx

168 lines
5.8 KiB
JavaScript

// Reference: https://github.com/WordPress/gutenberg/blob/d5ab7238e53d0947d4bb0853464b1c58325b6130/packages/edit-site/src/components/global-styles/style-variations-container.js
/**
* External dependencies
*/
import clsx from 'clsx';
import { useMemo, useContext } from '@wordpress/element';
import { ENTER } from '@wordpress/keycodes';
import { __, sprintf } from '@wordpress/i18n';
import {
privateApis as blockEditorPrivateApis,
BlockEditorProvider,
} from '@wordpress/block-editor';
import { mergeBaseAndUserConfigs } from '@wordpress/edit-site/build-module/components/global-styles/global-styles-provider';
import { unlock } from '@wordpress/edit-site/build-module/lock-unlock';
import { isEqual, noop } from 'lodash';
const { GlobalStylesContext } = unlock( blockEditorPrivateApis );
// 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;
};
// 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;
};
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 = () => {
// Remove the hasCreatedOwnColors flag if the user is switching to a color palette
// hasCreatedOwnColors flag is used for visually deselecting preset color palettes if user has created their own
if (
variation.settings.color &&
user.settings.color &&
user.settings.color.palette.hasCreatedOwnColors
) {
delete user.settings.color.palette.hasCreatedOwnColors;
// 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;
}
}
}
const resetTypographySettingsStyles = resetTypographySettings(
variation,
user.styles
);
const resetStyleSettingsStyles = resetStyleSettings(
variation,
resetTypographySettingsStyles
);
setUserConfig( () => {
return {
settings: mergeBaseAndUserConfigs(
user.settings,
variation.settings
),
styles: mergeBaseAndUserConfigs(
resetStyleSettingsStyles,
variation.styles
),
};
} );
};
const selectOnEnter = ( event ) => {
if ( event.keyCode === ENTER ) {
event.preventDefault();
selectVariation();
}
};
const isActive = useMemo( () => {
if ( variation.settings.color ) {
return isEqual( variation.settings.color, user.settings.color );
}
// 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;
return (
variation.settings.typography?.fontFamilies.theme.every(
( { slug } ) =>
theme.some( ( { slug: themeSlug } ) => themeSlug === slug )
) &&
theme.length ===
variation.settings.typography?.fontFamilies.theme.length
);
}, [ 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 (
<BlockEditorProvider
onChange={ noop }
onInput={ noop }
settings={ {} }
useSubRegistry={ true }
>
<GlobalStylesContext.Provider value={ context }>
<div
className={ clsx(
'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>
</div>
</GlobalStylesContext.Provider>
</BlockEditorProvider>
);
};