diff --git a/packages/edit-site/src/components/editor/global-styles-provider.js b/packages/edit-site/src/components/editor/global-styles-provider.js
deleted file mode 100644
index 5050be93563a82..00000000000000
--- a/packages/edit-site/src/components/editor/global-styles-provider.js
+++ /dev/null
@@ -1,334 +0,0 @@
-/**
- * External dependencies
- */
-import { set, get, has, mergeWith, mapValues, setWith, clone } from 'lodash';
-
-/**
- * WordPress dependencies
- */
-import {
- createContext,
- useCallback,
- useContext,
- useEffect,
- useMemo,
-} from '@wordpress/element';
-import {
- __EXPERIMENTAL_STYLE_PROPERTY as STYLE_PROPERTY,
- __EXPERIMENTAL_ELEMENTS as ELEMENTS,
- store as blocksStore,
-} from '@wordpress/blocks';
-import { useEntityProp } from '@wordpress/core-data';
-import { useSelect, useDispatch } from '@wordpress/data';
-
-/**
- * Internal dependencies
- */
-import {
- ROOT_BLOCK_NAME,
- ROOT_BLOCK_SELECTOR,
- ROOT_BLOCK_SUPPORTS,
- getValueFromVariable,
- getPresetVariable,
- PRESET_METADATA,
-} from './utils';
-import { toCustomProperties, toStyles } from './global-styles-renderer';
-import { store as editSiteStore } from '../../store';
-
-const EMPTY_CONTENT = { isGlobalStylesUserThemeJSON: true, version: 1 };
-const EMPTY_CONTENT_STRING = JSON.stringify( EMPTY_CONTENT );
-
-const GlobalStylesContext = createContext( {
- /* eslint-disable no-unused-vars */
- getSetting: ( context, path ) => {},
- setSetting: ( context, path, newValue ) => {},
- getStyle: ( context, propertyName, origin ) => {},
- setStyle: ( context, propertyName, newValue ) => {},
- contexts: {},
- /* eslint-enable no-unused-vars */
-} );
-
-const mergeTreesCustomizer = ( objValue, srcValue ) => {
- // We only pass as arrays the presets,
- // in which case we want the new array of values
- // to override the old array (no merging).
- if ( Array.isArray( srcValue ) ) {
- return srcValue;
- }
-};
-
-export const useGlobalStylesContext = () => useContext( GlobalStylesContext );
-
-const useGlobalStylesEntityContent = () => {
- return useEntityProp( 'postType', 'wp_global_styles', 'content' );
-};
-
-export const useGlobalStylesReset = () => {
- const [ content, setContent ] = useGlobalStylesEntityContent();
- const canRestart = !! content && content !== EMPTY_CONTENT_STRING;
- return [
- canRestart,
- useCallback( () => setContent( EMPTY_CONTENT_STRING ), [ setContent ] ),
- ];
-};
-
-const extractSupportKeys = ( supports ) => {
- const supportKeys = [];
- Object.keys( STYLE_PROPERTY ).forEach( ( name ) => {
- if ( ! STYLE_PROPERTY[ name ].support ) {
- return;
- }
-
- // Opting out means that, for certain support keys like background color,
- // blocks have to explicitly set the support value false. If the key is
- // unset, we still enable it.
- if ( STYLE_PROPERTY[ name ].requiresOptOut ) {
- if (
- has( supports, STYLE_PROPERTY[ name ].support[ 0 ] ) &&
- get( supports, STYLE_PROPERTY[ name ].support ) !== false
- ) {
- return supportKeys.push( name );
- }
- }
-
- if ( get( supports, STYLE_PROPERTY[ name ].support, false ) ) {
- return supportKeys.push( name );
- }
- } );
- return supportKeys;
-};
-
-const getBlockMetadata = ( blockTypes ) => {
- const result = {};
-
- blockTypes.forEach( ( blockType ) => {
- const name = blockType.name;
- const supports = extractSupportKeys( blockType?.supports );
-
- const selector =
- blockType?.supports?.__experimentalSelector ??
- '.wp-block-' + name.replace( 'core/', '' ).replace( '/', '-' );
- const blockSelectors = selector.split( ',' );
- const elements = [];
- Object.keys( ELEMENTS ).forEach( ( key ) => {
- const elementSelector = [];
- blockSelectors.forEach( ( blockSelector ) => {
- elementSelector.push( blockSelector + ' ' + ELEMENTS[ key ] );
- } );
- elements[ key ] = elementSelector.join( ',' );
- } );
- result[ name ] = {
- name,
- selector,
- supports,
- elements,
- };
- } );
-
- return result;
-};
-
-function immutableSet( object, path, value ) {
- return setWith( object ? clone( object ) : {}, path, value, clone );
-}
-
-export default function GlobalStylesProvider( { children, baseStyles } ) {
- const [ content, setContent ] = useGlobalStylesEntityContent();
- const { blockTypes, settings } = useSelect( ( select ) => {
- return {
- blockTypes: select( blocksStore ).getBlockTypes(),
- settings: select( editSiteStore ).getSettings(),
- };
- }, [] );
- const { updateSettings } = useDispatch( editSiteStore );
-
- const blocks = useMemo( () => getBlockMetadata( blockTypes ), [
- blockTypes,
- ] );
-
- const { __experimentalGlobalStylesBaseStyles: themeStyles } = settings;
- const { userStyles, mergedStyles } = useMemo( () => {
- let newUserStyles;
- try {
- newUserStyles = content ? JSON.parse( content ) : EMPTY_CONTENT;
-
- // At the moment, we ignore previous user config that
- // is in a different version than the theme config.
- if ( newUserStyles?.version !== baseStyles?.version ) {
- newUserStyles = EMPTY_CONTENT;
- }
- } catch ( e ) {
- /* eslint-disable no-console */
- console.error( 'User data is not JSON' );
- console.error( e );
- /* eslint-enable no-console */
- newUserStyles = EMPTY_CONTENT;
- }
-
- // It is very important to verify if the flag isGlobalStylesUserThemeJSON is true.
- // If it is not true the content was not escaped and is not safe.
- if ( ! newUserStyles.isGlobalStylesUserThemeJSON ) {
- newUserStyles = EMPTY_CONTENT;
- }
-
- const addUserToSettings = ( settingsToAdd ) => {
- PRESET_METADATA.forEach( ( { path } ) => {
- const presetData = get( settingsToAdd, path );
- if ( presetData ) {
- settingsToAdd = immutableSet( settingsToAdd, path, {
- user: presetData,
- } );
- }
- } );
- return settingsToAdd;
- };
-
- let userStylesWithOrigin = newUserStyles;
- if ( userStylesWithOrigin.settings ) {
- userStylesWithOrigin = {
- ...userStylesWithOrigin,
- settings: addUserToSettings( userStylesWithOrigin.settings ),
- };
- if ( userStylesWithOrigin.settings.blocks ) {
- userStylesWithOrigin.settings = {
- ...userStylesWithOrigin.settings,
- blocks: mapValues(
- userStylesWithOrigin.settings.blocks,
- addUserToSettings
- ),
- };
- }
- }
-
- // At this point, the version schema of the theme & user
- // is the same, so we can merge them.
- const newMergedStyles = mergeWith(
- {},
- baseStyles,
- userStylesWithOrigin,
- mergeTreesCustomizer
- );
-
- return {
- userStyles: newUserStyles,
- mergedStyles: newMergedStyles,
- };
- }, [ content ] );
-
- const nextValue = useMemo(
- () => ( {
- root: {
- name: ROOT_BLOCK_NAME,
- selector: ROOT_BLOCK_SELECTOR,
- supports: ROOT_BLOCK_SUPPORTS,
- elements: ELEMENTS,
- },
- blocks,
- getSetting: ( context, propertyPath ) => {
- const path =
- context === ROOT_BLOCK_NAME
- ? propertyPath
- : [ 'blocks', context, ...propertyPath ];
- get( userStyles?.settings, path );
- },
- setSetting: ( context, propertyPath, newValue ) => {
- const newContent = { ...userStyles };
- const path =
- context === ROOT_BLOCK_NAME
- ? [ 'settings' ]
- : [ 'settings', 'blocks', context ];
-
- let newSettings = get( newContent, path );
- if ( ! newSettings ) {
- newSettings = {};
- set( newContent, path, newSettings );
- }
- set( newSettings, propertyPath, newValue );
-
- setContent( JSON.stringify( newContent ) );
- },
- getStyle: ( context, propertyName, origin = 'merged' ) => {
- const propertyPath = STYLE_PROPERTY[ propertyName ].value;
- const path =
- context === ROOT_BLOCK_NAME
- ? propertyPath
- : [ 'blocks', context, ...propertyPath ];
-
- if ( origin === 'theme' ) {
- const value = get( themeStyles?.styles, path );
- return getValueFromVariable( themeStyles, context, value );
- }
-
- if ( origin === 'user' ) {
- const value = get( userStyles?.styles, path );
-
- // We still need to use merged styles here because the
- // presets used to resolve user variable may be defined a
- // layer down ( core, theme, or user ).
- return getValueFromVariable( mergedStyles, context, value );
- }
-
- const value = get( mergedStyles?.styles, path );
- return getValueFromVariable( mergedStyles, context, value );
- },
- setStyle: ( context, propertyName, newValue ) => {
- const newContent = { ...userStyles };
-
- const path =
- ROOT_BLOCK_NAME === context
- ? [ 'styles' ]
- : [ 'styles', 'blocks', context ];
- const propertyPath = STYLE_PROPERTY[ propertyName ].value;
-
- let newStyles = get( newContent, path );
- if ( ! newStyles ) {
- newStyles = {};
- set( newContent, path, newStyles );
- }
- set(
- newStyles,
- propertyPath,
- getPresetVariable(
- mergedStyles,
- context,
- propertyName,
- newValue
- )
- );
-
- setContent( JSON.stringify( newContent ) );
- },
- } ),
- [ content, mergedStyles, themeStyles ]
- );
-
- useEffect( () => {
- const nonGlobalStyles = settings.styles.filter(
- ( style ) => ! style.isGlobalStyles
- );
- const customProperties = toCustomProperties( mergedStyles, blocks );
- const globalStyles = toStyles( mergedStyles, blocks );
- updateSettings( {
- ...settings,
- styles: [
- ...nonGlobalStyles,
- {
- css: customProperties,
- isGlobalStyles: true,
- __experimentalNoWrapper: true,
- },
- {
- css: globalStyles,
- isGlobalStyles: true,
- },
- ],
- __experimentalFeatures: mergedStyles.settings,
- } );
- }, [ blocks, mergedStyles ] );
-
- return (
-
- { children }
-
- );
-}
diff --git a/packages/edit-site/src/components/editor/global-styles-renderer.js b/packages/edit-site/src/components/editor/global-styles-renderer.js
index fdec33c3fc0230..27988d56a60e6f 100644
--- a/packages/edit-site/src/components/editor/global-styles-renderer.js
+++ b/packages/edit-site/src/components/editor/global-styles-renderer.js
@@ -1,334 +1,37 @@
-/**
- * External dependencies
- */
-import {
- first,
- forEach,
- get,
- isEmpty,
- isString,
- kebabCase,
- pickBy,
- reduce,
- set,
- startsWith,
-} from 'lodash';
-
/**
* WordPress dependencies
*/
-import {
- __EXPERIMENTAL_STYLE_PROPERTY as STYLE_PROPERTY,
- __EXPERIMENTAL_ELEMENTS as ELEMENTS,
-} from '@wordpress/blocks';
+import { useEffect } from '@wordpress/element';
+import { useSelect, useDispatch } from '@wordpress/data';
/**
* Internal dependencies
*/
-import { PRESET_METADATA, ROOT_BLOCK_SELECTOR } from './utils';
-
-function compileStyleValue( uncompiledValue ) {
- const VARIABLE_REFERENCE_PREFIX = 'var:';
- const VARIABLE_PATH_SEPARATOR_TOKEN_ATTRIBUTE = '|';
- const VARIABLE_PATH_SEPARATOR_TOKEN_STYLE = '--';
- if ( startsWith( uncompiledValue, VARIABLE_REFERENCE_PREFIX ) ) {
- const variable = uncompiledValue
- .slice( VARIABLE_REFERENCE_PREFIX.length )
- .split( VARIABLE_PATH_SEPARATOR_TOKEN_ATTRIBUTE )
- .join( VARIABLE_PATH_SEPARATOR_TOKEN_STYLE );
- return `var(--wp--${ variable })`;
- }
- return uncompiledValue;
-}
+import { store as editSiteStore } from '../../store';
/**
- * Transform given preset tree into a set of style declarations.
- *
- * @param {Object} blockPresets
- *
- * @return {Array} An array of style declarations.
- */
-function getPresetsDeclarations( blockPresets = {} ) {
- return reduce(
- PRESET_METADATA,
- ( declarations, { path, valueKey, cssVarInfix } ) => {
- const presetByOrigin = get( blockPresets, path, [] );
- [ 'core', 'theme', 'user' ].forEach( ( origin ) => {
- if ( presetByOrigin[ origin ] ) {
- presetByOrigin[ origin ].forEach( ( value ) => {
- declarations.push(
- `--wp--preset--${ cssVarInfix }--${ kebabCase(
- value.slug
- ) }: ${ value[ valueKey ] }`
- );
- } );
- }
- } );
-
- return declarations;
- },
- []
- );
-}
-
-/**
- * Transform given preset tree into a set of preset class declarations.
- *
- * @param {string} blockSelector
- * @param {Object} blockPresets
- * @return {string} CSS declarations for the preset classes.
+ * Internal dependencies
*/
-function getPresetsClasses( blockSelector, blockPresets = {} ) {
- return reduce(
- PRESET_METADATA,
- ( declarations, { path, cssVarInfix, classes } ) => {
- if ( ! classes ) {
- return declarations;
- }
+import { useGlobalStylesOutput } from '../global-styles/use-global-styles-output';
- const presetByOrigin = get( blockPresets, path, [] );
- [ 'core', 'theme', 'user' ].forEach( ( origin ) => {
- if ( presetByOrigin[ origin ] ) {
- presetByOrigin[ origin ].forEach( ( { slug } ) => {
- classes.forEach( ( { classSuffix, propertyName } ) => {
- const classSelectorToUse = `.has-${ kebabCase(
- slug
- ) }-${ classSuffix }`;
- const selectorToUse = blockSelector
- .split( ',' ) // Selector can be "h1, h2, h3"
- .map(
- ( selector ) =>
- `${ selector }${ classSelectorToUse }`
- )
- .join( ',' );
- const value = `var(--wp--preset--${ cssVarInfix }--${ kebabCase(
- slug
- ) })`;
- declarations += `${ selectorToUse }{${ propertyName }: ${ value } !important;}`;
- } );
- } );
- }
- } );
- return declarations;
- },
- ''
- );
-}
+export function useGlobalStylesRenderer() {
+ const [ styles, settings ] = useGlobalStylesOutput();
+ const { getSettings } = useSelect( editSiteStore );
+ const { updateSettings } = useDispatch( editSiteStore );
-function flattenTree( input = {}, prefix, token ) {
- let result = [];
- Object.keys( input ).forEach( ( key ) => {
- const newKey = prefix + kebabCase( key.replace( '/', '-' ) );
- const newLeaf = input[ key ];
-
- if ( newLeaf instanceof Object ) {
- const newPrefix = newKey + token;
- result = [ ...result, ...flattenTree( newLeaf, newPrefix, token ) ];
- } else {
- result.push( `${ newKey }: ${ newLeaf }` );
+ useEffect( () => {
+ if ( ! styles || ! settings ) {
+ return;
}
- } );
- return result;
-}
-/**
- * Transform given style tree into a set of style declarations.
- *
- * @param {Object} blockStyles Block styles.
- *
- * @return {Array} An array of style declarations.
- */
-function getStylesDeclarations( blockStyles = {} ) {
- return reduce(
- STYLE_PROPERTY,
- ( declarations, { value, properties }, key ) => {
- const pathToValue = value;
- if ( first( pathToValue ) === 'elements' ) {
- return declarations;
- }
-
- const styleValue = get( blockStyles, pathToValue );
-
- if ( !! properties && ! isString( styleValue ) ) {
- Object.entries( properties ).forEach( ( entry ) => {
- const [ name, prop ] = entry;
-
- if ( ! get( styleValue, [ prop ], false ) ) {
- // Do not create a declaration
- // for sub-properties that don't have any value.
- return;
- }
-
- const cssProperty = kebabCase( name );
- declarations.push(
- `${ cssProperty }: ${ compileStyleValue(
- get( styleValue, [ prop ] )
- ) }`
- );
- } );
- } else if ( get( blockStyles, pathToValue, false ) ) {
- const cssProperty = key.startsWith( '--' )
- ? key
- : kebabCase( key );
- declarations.push(
- `${ cssProperty }: ${ compileStyleValue(
- get( blockStyles, pathToValue )
- ) }`
- );
- }
-
- return declarations;
- },
- []
- );
-}
-
-export const getNodesWithStyles = ( tree, blockSelectors ) => {
- const nodes = [];
-
- if ( ! tree?.styles ) {
- return nodes;
- }
-
- const pickStyleKeys = ( treeToPickFrom ) =>
- pickBy( treeToPickFrom, ( value, key ) =>
- [ 'border', 'color', 'spacing', 'typography' ].includes( key )
+ const currentStoreSettings = getSettings();
+ const nonGlobalStyles = currentStoreSettings?.styles?.filter(
+ ( style ) => ! style.isGlobalStyles
);
-
- // Top-level.
- const styles = pickStyleKeys( tree.styles );
- if ( !! styles ) {
- nodes.push( {
- styles,
- selector: ROOT_BLOCK_SELECTOR,
+ updateSettings( {
+ ...currentStoreSettings,
+ styles: [ ...nonGlobalStyles, ...styles ],
+ __experimentalFeatures: settings,
} );
- }
- forEach( tree.styles?.elements, ( value, key ) => {
- if ( !! value && !! ELEMENTS[ key ] ) {
- nodes.push( {
- styles: value,
- selector: ELEMENTS[ key ],
- } );
- }
- } );
-
- // Iterate over blocks: they can have styles & elements.
- forEach( tree.styles?.blocks, ( node, blockName ) => {
- const blockStyles = pickStyleKeys( node );
- if ( !! blockStyles && !! blockSelectors?.[ blockName ]?.selector ) {
- nodes.push( {
- styles: blockStyles,
- selector: blockSelectors[ blockName ].selector,
- } );
- }
-
- forEach( node?.elements, ( value, elementName ) => {
- if (
- !! value &&
- !! blockSelectors?.[ blockName ]?.elements?.[ elementName ]
- ) {
- nodes.push( {
- styles: value,
- selector:
- blockSelectors[ blockName ].elements[ elementName ],
- } );
- }
- } );
- } );
-
- return nodes;
-};
-
-export const getNodesWithSettings = ( tree, blockSelectors ) => {
- const nodes = [];
-
- if ( ! tree?.settings ) {
- return nodes;
- }
-
- const pickPresets = ( treeToPickFrom ) => {
- const presets = {};
- PRESET_METADATA.forEach( ( { path } ) => {
- const value = get( treeToPickFrom, path, false );
- if ( value !== false ) {
- set( presets, path, value );
- }
- } );
- return presets;
- };
-
- // Top-level.
- const presets = pickPresets( tree.settings );
- const custom = tree.settings?.custom;
- if ( ! isEmpty( presets ) || !! custom ) {
- nodes.push( {
- presets,
- custom,
- selector: ROOT_BLOCK_SELECTOR,
- } );
- }
-
- // Blocks.
- forEach( tree.settings?.blocks, ( node, blockName ) => {
- const blockPresets = pickPresets( node );
- const blockCustom = node.custom;
- if ( ! isEmpty( blockPresets ) || !! blockCustom ) {
- nodes.push( {
- presets: blockPresets,
- custom: blockCustom,
- selector: blockSelectors[ blockName ].selector,
- } );
- }
- } );
-
- return nodes;
-};
-
-export const toCustomProperties = ( tree, blockSelectors ) => {
- const settings = getNodesWithSettings( tree, blockSelectors );
-
- let ruleset = '';
- settings.forEach( ( { presets, custom, selector } ) => {
- const declarations = getPresetsDeclarations( presets );
- const customProps = flattenTree( custom, '--wp--custom--', '--' );
- if ( customProps.length > 0 ) {
- declarations.push( ...customProps );
- }
-
- if ( declarations.length > 0 ) {
- ruleset = ruleset + `${ selector }{${ declarations.join( ';' ) };}`;
- }
- } );
-
- return ruleset;
-};
-
-export const toStyles = ( tree, blockSelectors ) => {
- const nodesWithStyles = getNodesWithStyles( tree, blockSelectors );
- const nodesWithSettings = getNodesWithSettings( tree, blockSelectors );
-
- let ruleset =
- '.wp-site-blocks > * + * { margin-top: var( --wp--style--block-gap ); margin-bottom: 0; }';
- nodesWithStyles.forEach( ( { selector, styles } ) => {
- const declarations = getStylesDeclarations( styles );
-
- if ( declarations.length === 0 ) {
- return;
- }
- ruleset = ruleset + `${ selector }{${ declarations.join( ';' ) };}`;
- } );
-
- nodesWithSettings.forEach( ( { selector, presets } ) => {
- if ( ROOT_BLOCK_SELECTOR === selector ) {
- // Do not add extra specificity for top-level classes.
- selector = '';
- }
-
- const classes = getPresetsClasses( selector, presets );
- if ( ! isEmpty( classes ) ) {
- ruleset = ruleset + classes;
- }
- } );
-
- return ruleset;
-};
+ }, [ styles, settings ] );
+}
diff --git a/packages/edit-site/src/components/editor/index.js b/packages/edit-site/src/components/editor/index.js
index c2358904049d9f..525753c34f7bd3 100644
--- a/packages/edit-site/src/components/editor/index.js
+++ b/packages/edit-site/src/components/editor/index.js
@@ -35,13 +35,13 @@ import Header from '../header';
import { SidebarComplementaryAreaFills } from '../sidebar';
import BlockEditor from '../block-editor';
import KeyboardShortcuts from '../keyboard-shortcuts';
-import GlobalStylesProvider from './global-styles-provider';
import NavigationSidebar from '../navigation-sidebar';
import URLQueryController from '../url-query-controller';
import InserterSidebar from '../secondary-sidebar/inserter-sidebar';
import ListViewSidebar from '../secondary-sidebar/list-view-sidebar';
import ErrorBoundary from '../error-boundary';
import { store as editSiteStore } from '../../store';
+import { useGlobalStylesRenderer } from './global-styles-renderer';
const interfaceLabels = {
secondarySidebar: __( 'Block Library' ),
@@ -159,6 +159,8 @@ function Editor( { initialSettings, onError } ) {
}
}, [ isNavigationOpen ] );
+ useGlobalStylesRenderer();
+
// Don't render the Editor until the settings are set and loaded
if ( ! settings?.siteUrl ) {
return null;
@@ -184,105 +186,87 @@ function Editor( { initialSettings, onError } ) {
type={ templateType }
id={ entityId }
>
-
-
-
+
+
+
+
+
+ }
+ secondarySidebar={ secondarySidebar() }
+ sidebar={
+ sidebarIsOpened && (
+
+ )
}
- >
-
-
-
-
-
- }
- secondarySidebar={ secondarySidebar() }
- sidebar={
- sidebarIsOpened && (
-
- )
+ header={
+
+ }
+ notices={ }
+ content={
+ <>
+
+ { template && (
+
- }
- notices={ }
- content={
- <>
-
- { template && (
-
- ) }
- { templateResolved &&
- ! template &&
- settings?.siteUrl &&
- entityId && (
-
- { __(
- "You attempted to edit an item that doesn't exist. Perhaps it was deleted?"
- ) }
-
+ ) }
+ { templateResolved &&
+ ! template &&
+ settings?.siteUrl &&
+ entityId && (
+
+ { __(
+ "You attempted to edit an item that doesn't exist. Perhaps it was deleted?"
) }
-
- >
- }
- actions={
- <>
- { isEntitiesSavedStatesOpen ? (
-
- ) : (
-
-
-
- ) }
- >
- }
- footer={ }
- />
-
-
-
-
-
-
+
+ ) }
+
+ >
+ }
+ actions={
+ <>
+ { isEntitiesSavedStatesOpen ? (
+
+ ) : (
+
+
+
+ ) }
+ >
+ }
+ footer={ }
+ />
+
+
+
+
diff --git a/packages/edit-site/src/components/editor/test/global-styles-provider.js b/packages/edit-site/src/components/editor/test/global-styles-provider.js
deleted file mode 100644
index 0a31516576e572..00000000000000
--- a/packages/edit-site/src/components/editor/test/global-styles-provider.js
+++ /dev/null
@@ -1,131 +0,0 @@
-/**
- * WordPress dependencies
- */
-import { dispatch } from '@wordpress/data';
-
-/**
- * External dependencies
- */
-import { mount } from 'enzyme';
-import { act } from 'react-dom/test-utils';
-
-/**
- * Internal dependencies
- */
-import GlobalStylesProvider, {
- useGlobalStylesContext,
-} from '../global-styles-provider';
-
-const settings = {
- styles: [
- {
- css: 'body {\n\tmargin: 0;\n\tpadding: 0;\n}',
- baseURL: 'http://localhost:4759/ponyfill.css',
- },
- ],
- __experimentalGlobalStylesBaseStyles: {},
-};
-
-const generateCoverBlockType = ( colorSupports ) => {
- return {
- name: 'core/cover',
- supports: {
- color: colorSupports,
- },
- };
-};
-
-const FakeCmp = () => {
- const globalStylesContext = useGlobalStylesContext();
- const coverBlockSupports =
- globalStylesContext.blocks[ 'core/cover' ].supports;
-
- return ;
-};
-
-const generateWrapper = () => {
- return mount(
-
-
-
- );
-};
-
-describe( 'global styles provider', () => {
- beforeAll( () => {
- dispatch( 'core/edit-site' ).updateSettings( settings );
- } );
-
- describe( 'when a block enables color support', () => {
- describe( 'and disables background color support', () => {
- it( 'still enables text color support', () => {
- act( () => {
- dispatch( 'core/blocks' ).addBlockTypes(
- generateCoverBlockType( {
- link: true,
- background: false,
- } )
- );
- } );
-
- const wrapper = generateWrapper();
- const actual = wrapper
- .findWhere( ( ele ) => Boolean( ele.prop( 'supports' ) ) )
- .prop( 'supports' );
- expect( actual ).not.toContain( 'backgroundColor' );
- expect( actual ).toContain( 'color' );
-
- act( () => {
- dispatch( 'core/blocks' ).removeBlockTypes( 'core/cover' );
- } );
- } );
- } );
-
- describe( 'and both text color and background color support are disabled', () => {
- it( 'disables text color and background color support', () => {
- act( () => {
- dispatch( 'core/blocks' ).addBlockTypes(
- generateCoverBlockType( {
- text: false,
- background: false,
- } )
- );
- } );
-
- const wrapper = generateWrapper();
- const actual = wrapper
- .findWhere( ( ele ) => Boolean( ele.prop( 'supports' ) ) )
- .prop( 'supports' );
- expect( actual ).not.toContain( 'backgroundColor' );
- expect( actual ).not.toContain( 'color' );
-
- act( () => {
- dispatch( 'core/blocks' ).removeBlockTypes( 'core/cover' );
- } );
- } );
- } );
-
- describe( 'and text color and background color supports are omitted', () => {
- it( 'still enables both text color and background color supports', () => {
- act( () => {
- dispatch( 'core/blocks' ).addBlockTypes(
- generateCoverBlockType( { link: true } )
- );
- } );
-
- const wrapper = generateWrapper();
- const actual = wrapper
- .findWhere( ( ele ) => Boolean( ele.prop( 'supports' ) ) )
- .prop( 'supports' );
- expect( actual ).toContain( 'backgroundColor' );
- expect( actual ).toContain( 'color' );
-
- act( () => {
- dispatch( 'core/blocks' ).removeBlockTypes( 'core/cover' );
- } );
- } );
- } );
- } );
-} );
diff --git a/packages/edit-site/src/components/global-styles/README.md b/packages/edit-site/src/components/global-styles/README.md
new file mode 100644
index 00000000000000..ac728f3eb8bf52
--- /dev/null
+++ b/packages/edit-site/src/components/global-styles/README.md
@@ -0,0 +1,91 @@
+# Global Styles
+
+This folder contains all the necessary APIs to render the global styles UI and to manipulate the global styles data. It can be potentially extracted to its own package.
+
+# Available public APIs
+
+## GlobalStylesUI
+
+A component used to render the Global Styles UI. It's current used in the sidebar of the site editor.
+
+```js
+import { GlobalStylesUI } from './global-styles';
+
+function MyComponent() {
+ return ;
+}
+```
+
+## useGlobalStylesReset
+
+A React hook used to retrieve whether the Global Styles have been edited and a callback to reset to the default theme values.
+
+```js
+import { useGlobalStylesReset } from './global-styles';
+
+function MyComponent() {
+ const [ canReset, reset ] = useGlobalStylesReset();
+
+ return canReset
+ ?
+ : null;
+}
+```
+
+## useGlobalStylesOutput
+
+A React hook used to retrieve the styles array and settings to provide for block editor instances based on the current global styles.
+
+```js
+import { useGlobalStylesOutput } from './global-styles';
+import { BlockEditorProvider, BlockList } from '@wordpress/block-editor';
+
+function MyComponent() {
+ const [ styles, settings ] = useGlobalStylesOutput();
+
+ return
+
+
+}
+```
+
+## useStyle
+
+A react hook used to retrieve the style applied to a given context.
+
+```js
+import { useStyle } from './global-styles';
+
+function MyComponent() {
+ // Text color for the site root.
+ const [ color, setColor ] = useStyle( 'color.text' );
+
+ // The user modified color for the core paragraph block.
+ const [ pColor, setPColor ] = useStyle( 'color.text', 'core/paragraph', 'user' );
+
+ return "Something";
+}
+```
+
+## useSetting
+
+A react hook used to retrieve the setting applied to a given context.
+
+```js
+import { useSetting } from './global-styles';
+
+function MyComponent() {
+ // The default color palette.
+ const [ colorPalette, setColorPalette ] = useSetting( 'color.palette' );
+
+ // The base (theme + core) color palette for the paragraph block,
+ // ignoring user provided palette.
+ // If the palette is not defined for the paragraph block, the root one is returned.
+ const [ pColor, setPColor ] = useSetting( 'color.palette', 'core/paragraph', 'base' );
+
+ return "Something";
+}
+```
diff --git a/packages/edit-site/src/components/global-styles/border-panel.js b/packages/edit-site/src/components/global-styles/border-panel.js
index 19cdcff68c6eed..2dd6675debe239 100644
--- a/packages/edit-site/src/components/global-styles/border-panel.js
+++ b/packages/edit-site/src/components/global-styles/border-panel.js
@@ -16,7 +16,7 @@ import { __ } from '@wordpress/i18n';
/**
* Internal dependencies
*/
-import { useSetting } from '../editor/utils';
+import { getSupportedGlobalStylesPanels, useSetting, useStyle } from './hooks';
const MIN_BORDER_WIDTH = 0;
@@ -24,72 +24,82 @@ const MIN_BORDER_WIDTH = 0;
// color control.
const EMPTY_ARRAY = [];
-export function useHasBorderPanel( { supports, name } ) {
+export function useHasBorderPanel( name ) {
const controls = [
- useHasBorderColorControl( { supports, name } ),
- useHasBorderRadiusControl( { supports, name } ),
- useHasBorderStyleControl( { supports, name } ),
- useHasBorderWidthControl( { supports, name } ),
+ useHasBorderColorControl( name ),
+ useHasBorderRadiusControl( name ),
+ useHasBorderStyleControl( name ),
+ useHasBorderWidthControl( name ),
];
return controls.some( Boolean );
}
-function useHasBorderColorControl( { supports, name } ) {
+function useHasBorderColorControl( name ) {
+ const supports = getSupportedGlobalStylesPanels( name );
return (
- useSetting( 'border.customColor', name ) &&
+ useSetting( 'border.customColor', name )[ 0 ] &&
supports.includes( 'borderColor' )
);
}
-function useHasBorderRadiusControl( { supports, name } ) {
+function useHasBorderRadiusControl( name ) {
+ const supports = getSupportedGlobalStylesPanels( name );
return (
- useSetting( 'border.customRadius', name ) &&
+ useSetting( 'border.customRadius', name )[ 0 ] &&
supports.includes( 'borderRadius' )
);
}
-function useHasBorderStyleControl( { supports, name } ) {
+function useHasBorderStyleControl( name ) {
+ const supports = getSupportedGlobalStylesPanels( name );
return (
- useSetting( 'border.customStyle', name ) &&
+ useSetting( 'border.customStyle', name )[ 0 ] &&
supports.includes( 'borderStyle' )
);
}
-function useHasBorderWidthControl( { supports, name } ) {
+function useHasBorderWidthControl( name ) {
+ const supports = getSupportedGlobalStylesPanels( name );
return (
- useSetting( 'border.customWidth', name ) &&
+ useSetting( 'border.customWidth', name )[ 0 ] &&
supports.includes( 'borderWidth' )
);
}
-export default function BorderPanel( {
- context: { supports, name },
- getStyle,
- setStyle,
-} ) {
+export default function BorderPanel( { name } ) {
const units = useCustomUnits( {
- availableUnits: useSetting( 'spacing.units' ) || [ 'px', 'em', 'rem' ],
+ availableUnits: useSetting( 'spacing.units' )[ 0 ] || [
+ 'px',
+ 'em',
+ 'rem',
+ ],
} );
// Border width.
- const hasBorderWidth = useHasBorderWidthControl( { supports, name } );
- const borderWidthValue = getStyle( name, 'borderWidth' );
+ const hasBorderWidth = useHasBorderWidthControl( name );
+ const [ borderWidthValue, setBorderWidth ] = useStyle(
+ 'border.width',
+ name
+ );
// Border style.
- const hasBorderStyle = useHasBorderStyleControl( { supports, name } );
- const borderStyle = getStyle( name, 'borderStyle' );
+ const hasBorderStyle = useHasBorderStyleControl( name );
+ const [ borderStyle, setBorderStyle ] = useStyle( 'border.style', name );
// Border color.
- const colors = useSetting( 'color.palette' ) || EMPTY_ARRAY;
- const disableCustomColors = ! useSetting( 'color.custom' );
- const disableCustomGradients = ! useSetting( 'color.customGradient' );
- const hasBorderColor = useHasBorderColorControl( { supports, name } );
- const borderColor = getStyle( name, 'borderColor' );
+ const [ colors = EMPTY_ARRAY ] = useSetting( 'color.palette' );
+ const disableCustomColors = ! useSetting( 'color.custom' )[ 0 ];
+ const disableCustomGradients = ! useSetting( 'color.customGradient' )[ 0 ];
+ const hasBorderColor = useHasBorderColorControl( name );
+ const [ borderColor, setBorderColor ] = useStyle( 'border.color', name );
// Border radius.
- const hasBorderRadius = useHasBorderRadiusControl( { supports, name } );
- const borderRadiusValues = getStyle( name, 'borderRadius' );
+ const hasBorderRadius = useHasBorderRadiusControl( name );
+ const [ borderRadiusValues, setBorderRadius ] = useStyle(
+ 'border.radius',
+ name
+ );
return (
@@ -101,11 +111,7 @@ export default function BorderPanel( {
label={ __( 'Width' ) }
min={ MIN_BORDER_WIDTH }
onChange={ ( value ) => {
- setStyle(
- name,
- 'borderWidth',
- value || undefined
- );
+ setBorderWidth( value || undefined );
} }
units={ units }
/>
@@ -113,9 +119,7 @@ export default function BorderPanel( {
{ hasBorderStyle && (
- setStyle( name, 'borderStyle', value )
- }
+ onChange={ setBorderStyle }
/>
) }
@@ -128,17 +132,13 @@ export default function BorderPanel( {
gradients={ undefined }
disableCustomColors={ disableCustomColors }
disableCustomGradients={ disableCustomGradients }
- onColorChange={ ( value ) =>
- setStyle( name, 'borderColor', value )
- }
+ onColorChange={ setBorderColor }
/>
) }
{ hasBorderRadius && (
- setStyle( name, 'borderRadius', value )
- }
+ onChange={ setBorderRadius }
/>
) }
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 ead8f64cc96d48..76a9a951b35951 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
@@ -1,20 +1,14 @@
-/**
- * External dependencies
- */
-import { get } from 'lodash';
-
/**
* WordPress dependencies
*/
import { __experimentalColorEdit as ColorEdit } from '@wordpress/components';
import { __ } from '@wordpress/i18n';
-import { useSelect } from '@wordpress/data';
+import { useMemo } from '@wordpress/element';
/**
* Internal dependencies
*/
-import { useSetting } from '../editor/utils';
-import { store as editSiteStore } from '../../store';
+import { useSetting } from './hooks';
/**
* Shared reference to an empty array for cases where it is important to avoid
@@ -27,49 +21,33 @@ import { store as editSiteStore } from '../../store';
*/
const EMPTY_ARRAY = [];
-export default function ColorPalettePanel( {
- contextName,
- getSetting,
- setSetting,
-} ) {
- const colors = useSetting( 'color.palette', contextName );
- const userColors = getSetting( contextName, 'color.palette' );
- const immutableColorSlugs = useSelect(
- ( select ) => {
- const baseStyles = select( editSiteStore ).getSettings()
- .__experimentalGlobalStylesBaseStyles;
- const contextualBasePalette = get( baseStyles, [
- 'settings',
- 'blocks',
- contextName,
- 'color',
- 'palette',
- ] );
- const globalPalette = get( baseStyles, [
- 'settings',
- 'color',
- 'palette',
- ] );
- const basePalette =
- contextualBasePalette?.theme ??
- contextualBasePalette?.core ??
- globalPalette?.theme ??
- globalPalette?.core;
- if ( ! basePalette ) {
- return EMPTY_ARRAY;
- }
- return basePalette.map( ( { slug } ) => slug );
- },
- [ contextName ]
+export default function ColorPalettePanel( { name } ) {
+ const [ colors, setColors ] = useSetting( 'color.palette', name );
+ const [ userColors ] = useSetting( 'color.palette', name, 'user' );
+ const [ baseGlobalPalette ] = useSetting(
+ 'color.palette',
+ undefined,
+ 'base'
);
+ const [ baseContextualPalette ] = useSetting(
+ 'color.palette',
+ name,
+ 'base'
+ );
+ const immutableColorSlugs = useMemo( () => {
+ const basePalette = baseContextualPalette ?? baseGlobalPalette;
+ if ( ! basePalette ) {
+ return EMPTY_ARRAY;
+ }
+ return basePalette.map( ( { slug } ) => slug );
+ }, [ baseContextualPalette, baseGlobalPalette ] );
+
return (
{
- setSetting( contextName, 'color.palette', newColors );
- } }
+ onChange={ setColors }
emptyUI={ __(
'Colors are empty! Add some colors to create your own color palette.'
) }
diff --git a/packages/edit-site/src/components/global-styles/color-panel.js b/packages/edit-site/src/components/global-styles/color-panel.js
index 97e6a1feaa698d..64d7ff33f1e6a6 100644
--- a/packages/edit-site/src/components/global-styles/color-panel.js
+++ b/packages/edit-site/src/components/global-styles/color-panel.js
@@ -8,10 +8,11 @@ import { __ } from '@wordpress/i18n';
* Internal dependencies
*/
-import { useSetting } from '../editor/utils';
+import { getSupportedGlobalStylesPanels, useSetting, useStyle } from './hooks';
import Palette from './palette';
-export function useHasColorPanel( { supports } ) {
+export function useHasColorPanel( name ) {
+ const supports = getSupportedGlobalStylesPanels( name );
return (
supports.includes( 'color' ) ||
supports.includes( 'backgroundColor' ) ||
@@ -20,21 +21,19 @@ export function useHasColorPanel( { supports } ) {
);
}
-export default function ColorPanel( {
- context: { supports, name },
- getStyle,
- setStyle,
-} ) {
- const solids = useSetting( 'color.palette', name );
- const gradients = useSetting( 'color.gradients', name );
- const areCustomSolidsEnabled = useSetting( 'color.custom', name );
- const areCustomGradientsEnabled = useSetting(
+export default function ColorPanel( { name } ) {
+ const supports = getSupportedGlobalStylesPanels( name );
+ const [ solids ] = useSetting( 'color.palette', name );
+ const [ gradients ] = useSetting( 'color.gradients', name );
+ const [ areCustomSolidsEnabled ] = useSetting( 'color.custom', name );
+ const [ areCustomGradientsEnabled ] = useSetting(
'color.customGradient',
name
);
- const isLinkEnabled = useSetting( 'color.link', name );
- const isTextEnabled = useSetting( 'color.text', name );
- const isBackgroundEnabled = useSetting( 'color.background', name );
+
+ const [ isLinkEnabled ] = useSetting( 'color.link', name );
+ const [ isTextEnabled ] = useSetting( 'color.text', name );
+ const [ isBackgroundEnabled ] = useSetting( 'color.background', name );
const hasLinkColor =
supports.includes( 'linkColor' ) &&
@@ -52,14 +51,34 @@ export default function ColorPanel( {
supports.includes( 'background' ) &&
( gradients.length > 0 || areCustomGradientsEnabled );
- const settings = [];
+ const [ color, setColor ] = useStyle( 'color.text', name );
+ const [ userColor ] = useStyle( 'color.text', name, 'user' );
+ const [ backgroundColor, setBackgroundColor ] = useStyle(
+ 'color.background',
+ name
+ );
+ const [ userBackgroundColor ] = useStyle(
+ 'color.background',
+ name,
+ 'user'
+ );
+ const [ gradient, setGradient ] = useStyle( 'color.gradient', name );
+ const [ userGradient ] = useStyle( 'color.gradient', name, 'user' );
+ const [ linkColor, setLinkColor ] = useStyle(
+ 'elements.link.color.text',
+ name
+ );
+ const [ userLinkColor ] = useStyle(
+ 'elements.link.color.text',
+ name,
+ 'user'
+ );
+ const settings = [];
if ( hasTextColor ) {
- const color = getStyle( name, 'color' );
- const userColor = getStyle( name, 'color', 'user' );
settings.push( {
colorValue: color,
- onColorChange: ( value ) => setStyle( name, 'color', value ),
+ onColorChange: setColor,
label: __( 'Text color' ),
clearable: color === userColor,
} );
@@ -67,12 +86,9 @@ export default function ColorPanel( {
let backgroundSettings = {};
if ( hasBackgroundColor ) {
- const backgroundColor = getStyle( name, 'backgroundColor' );
- const userBackgroundColor = getStyle( name, 'backgroundColor', 'user' );
backgroundSettings = {
colorValue: backgroundColor,
- onColorChange: ( value ) =>
- setStyle( name, 'backgroundColor', value ),
+ onColorChange: setBackgroundColor,
};
if ( backgroundColor ) {
backgroundSettings.clearable =
@@ -82,12 +98,9 @@ export default function ColorPanel( {
let gradientSettings = {};
if ( hasGradientColor ) {
- const gradient = getStyle( name, 'background' );
- const userGradient = getStyle( name, 'background', 'user' );
gradientSettings = {
gradientValue: gradient,
- onGradientChange: ( value ) =>
- setStyle( name, 'background', value ),
+ onGradientChange: setGradient,
};
if ( gradient ) {
gradientSettings.clearable = gradient === userGradient;
@@ -103,13 +116,11 @@ export default function ColorPanel( {
}
if ( hasLinkColor ) {
- const color = getStyle( name, 'linkColor' );
- const userColor = getStyle( name, 'linkColor', 'user' );
settings.push( {
- colorValue: color,
- onColorChange: ( value ) => setStyle( name, 'linkColor', value ),
+ colorValue: linkColor,
+ onColorChange: setLinkColor,
label: __( 'Link color' ),
- clearable: color === userColor,
+ clearable: linkColor === userLinkColor,
} );
}
diff --git a/packages/edit-site/src/components/global-styles/context-menu.js b/packages/edit-site/src/components/global-styles/context-menu.js
index c2a339a95046ee..7686945e2621f1 100644
--- a/packages/edit-site/src/components/global-styles/context-menu.js
+++ b/packages/edit-site/src/components/global-styles/context-menu.js
@@ -14,11 +14,11 @@ import { useHasDimensionsPanel } from './dimensions-panel';
import { useHasTypographyPanel } from './typography-panel';
import NavigationButton from './navigation-button';
-function ContextMenu( { context, parentMenu = '' } ) {
- const hasTypographyPanel = useHasTypographyPanel( context );
- const hasColorPanel = useHasColorPanel( context );
- const hasBorderPanel = useHasBorderPanel( context );
- const hasDimensionsPanel = useHasDimensionsPanel( context );
+function ContextMenu( { name, parentMenu = '' } ) {
+ const hasTypographyPanel = useHasTypographyPanel( name );
+ const hasColorPanel = useHasColorPanel( name );
+ const hasBorderPanel = useHasBorderPanel( name );
+ const hasDimensionsPanel = useHasDimensionsPanel( name );
const hasLayoutPanel = hasBorderPanel || hasDimensionsPanel;
return (
diff --git a/packages/edit-site/src/components/global-styles/dimensions-panel.js b/packages/edit-site/src/components/global-styles/dimensions-panel.js
index 9f30114697e464..77c2e319ef3ef5 100644
--- a/packages/edit-site/src/components/global-styles/dimensions-panel.js
+++ b/packages/edit-site/src/components/global-styles/dimensions-panel.js
@@ -14,32 +14,35 @@ import { __experimentalUseCustomSides as useCustomSides } from '@wordpress/block
/**
* Internal dependencies
*/
-import { useSetting } from '../editor/utils';
+import { getSupportedGlobalStylesPanels, useSetting, useStyle } from './hooks';
const AXIAL_SIDES = [ 'horizontal', 'vertical' ];
-export function useHasDimensionsPanel( context ) {
- const hasPadding = useHasPadding( context );
- const hasMargin = useHasMargin( context );
- const hasGap = useHasGap( context );
+export function useHasDimensionsPanel( name ) {
+ const hasPadding = useHasPadding( name );
+ const hasMargin = useHasMargin( name );
+ const hasGap = useHasGap( name );
return hasPadding || hasMargin || hasGap;
}
-function useHasPadding( { name, supports } ) {
- const settings = useSetting( 'spacing.customPadding', name );
+function useHasPadding( name ) {
+ const supports = getSupportedGlobalStylesPanels( name );
+ const [ settings ] = useSetting( 'spacing.customPadding', name );
return settings && supports.includes( 'padding' );
}
-function useHasMargin( { name, supports } ) {
- const settings = useSetting( 'spacing.customMargin', name );
+function useHasMargin( name ) {
+ const supports = getSupportedGlobalStylesPanels( name );
+ const [ settings ] = useSetting( 'spacing.customMargin', name );
return settings && supports.includes( 'margin' );
}
-function useHasGap( { name, supports } ) {
- const settings = useSetting( 'spacing.blockGap', name );
+function useHasGap( name ) {
+ const supports = getSupportedGlobalStylesPanels( name );
+ const [ settings ] = useSetting( 'spacing.blockGap', name );
return settings && supports.includes( '--wp--style--block-gap' );
}
@@ -82,13 +85,12 @@ function splitStyleValue( value ) {
return value;
}
-export default function DimensionsPanel( { context, getStyle, setStyle } ) {
- const { name } = context;
- const showPaddingControl = useHasPadding( context );
- const showMarginControl = useHasMargin( context );
- const showGapControl = useHasGap( context );
+export default function DimensionsPanel( { name } ) {
+ const showPaddingControl = useHasPadding( name );
+ const showMarginControl = useHasMargin( name );
+ const showGapControl = useHasGap( name );
const units = useCustomUnits( {
- availableUnits: useSetting( 'spacing.units', name ) || [
+ availableUnits: useSetting( 'spacing.units', name )[ 0 ] || [
'%',
'px',
'em',
@@ -97,7 +99,8 @@ export default function DimensionsPanel( { context, getStyle, setStyle } ) {
],
} );
- const paddingValues = splitStyleValue( getStyle( name, 'padding' ) );
+ const [ rawPadding, setRawPadding ] = useStyle( 'spacing.padding', name );
+ const paddingValues = splitStyleValue( rawPadding );
const paddingSides = useCustomSides( name, 'padding' );
const isAxialPadding =
paddingSides &&
@@ -105,13 +108,14 @@ export default function DimensionsPanel( { context, getStyle, setStyle } ) {
const setPaddingValues = ( newPaddingValues ) => {
const padding = filterValuesBySides( newPaddingValues, paddingSides );
- setStyle( name, 'padding', padding );
+ setRawPadding( padding );
};
const resetPaddingValue = () => setPaddingValues( {} );
const hasPaddingValue = () =>
!! paddingValues && Object.keys( paddingValues ).length;
- const marginValues = splitStyleValue( getStyle( name, 'margin' ) );
+ const [ rawMargin, setRawMargin ] = useStyle( 'spacing.margin', name );
+ const marginValues = splitStyleValue( rawMargin );
const marginSides = useCustomSides( name, 'margin' );
const isAxialMargin =
marginSides &&
@@ -119,17 +123,13 @@ export default function DimensionsPanel( { context, getStyle, setStyle } ) {
const setMarginValues = ( newMarginValues ) => {
const margin = filterValuesBySides( newMarginValues, marginSides );
- setStyle( name, 'margin', margin );
+ setRawMargin( margin );
};
const resetMarginValue = () => setMarginValues( {} );
const hasMarginValue = () =>
!! marginValues && Object.keys( marginValues ).length;
- const gapValue = getStyle( name, '--wp--style--block-gap' );
-
- const setGapValue = ( newGapValue ) => {
- setStyle( name, '--wp--style--block-gap', newGapValue );
- };
+ const [ gapValue, setGapValue ] = useStyle( 'spacing.blockGap', name );
const resetGapValue = () => setGapValue( undefined );
const hasGapValue = () => !! gapValue;
diff --git a/packages/edit-site/src/components/global-styles/hooks.js b/packages/edit-site/src/components/global-styles/hooks.js
new file mode 100644
index 00000000000000..8e807a53ecc531
--- /dev/null
+++ b/packages/edit-site/src/components/global-styles/hooks.js
@@ -0,0 +1,347 @@
+/**
+ * External dependencies
+ */
+import { get, cloneDeep, set, isEqual, has, mergeWith } from 'lodash';
+
+/**
+ * WordPress dependencies
+ */
+import { useMemo, useCallback } from '@wordpress/element';
+import { useSelect, useDispatch } from '@wordpress/data';
+import { store as coreStore } from '@wordpress/core-data';
+import {
+ getBlockType,
+ __EXPERIMENTAL_PATHS_WITH_MERGE as PATHS_WITH_MERGE,
+ __EXPERIMENTAL_STYLE_PROPERTY as STYLE_PROPERTY,
+} from '@wordpress/blocks';
+
+/**
+ * Internal dependencies
+ */
+import { store as editSiteStore } from '../../store';
+import {
+ PRESET_METADATA,
+ getValueFromVariable,
+ getPresetVariableFromValue,
+} from './utils';
+
+const EMPTY_CONFIG = { isGlobalStylesUserThemeJSON: true, version: 1 };
+
+function mergeTreesCustomizer( objValue, srcValue ) {
+ // We only pass as arrays the presets,
+ // in which case we want the new array of values
+ // to override the old array (no merging).
+ if ( Array.isArray( srcValue ) ) {
+ return srcValue;
+ }
+}
+
+function mergeBaseAndUserConfigs( base, user ) {
+ return mergeWith( {}, base, user, mergeTreesCustomizer );
+}
+
+function addUserOriginToSettings( settingsToAdd ) {
+ const newSettings = cloneDeep( settingsToAdd );
+ PRESET_METADATA.forEach( ( { path } ) => {
+ const presetData = get( newSettings, path );
+ if ( presetData ) {
+ set( newSettings, path, {
+ user: presetData,
+ } );
+ }
+ } );
+ return newSettings;
+}
+
+function removeUserOriginFromSettings( settingsToRemove ) {
+ const newSettings = cloneDeep( settingsToRemove );
+ PRESET_METADATA.forEach( ( { path } ) => {
+ const presetData = get( newSettings, path );
+ if ( presetData ) {
+ set( newSettings, path, ( presetData ?? {} ).user );
+ }
+ } );
+ return newSettings;
+}
+
+function useGlobalStylesUserConfig() {
+ const { globalStylesId, content } = useSelect( ( select ) => {
+ const _globalStylesId = select( editSiteStore ).getSettings()
+ .__experimentalGlobalStylesUserEntityId;
+ return {
+ globalStylesId: _globalStylesId,
+ content: select( coreStore ).getEditedEntityRecord(
+ 'postType',
+ 'wp_global_styles',
+ _globalStylesId
+ )?.content,
+ };
+ }, [] );
+ const { getEditedEntityRecord } = useSelect( coreStore );
+ const { editEntityRecord } = useDispatch( coreStore );
+
+ const parseContent = ( contentToParse ) => {
+ let parsedConfig;
+ try {
+ parsedConfig = contentToParse ? JSON.parse( contentToParse ) : {};
+ // It is very important to verify if the flag isGlobalStylesUserThemeJSON is true.
+ // If it is not true the content was not escaped and is not safe.
+ if ( ! parsedConfig.isGlobalStylesUserThemeJSON ) {
+ parsedConfig = {};
+ } else {
+ parsedConfig = {
+ ...parsedConfig,
+ settings: addUserOriginToSettings( parsedConfig.settings ),
+ };
+ }
+ } catch ( e ) {
+ /* eslint-disable no-console */
+ console.error( 'Global Styles User data is not valid' );
+ console.error( e );
+ /* eslint-enable no-console */
+ parsedConfig = {};
+ }
+
+ return parsedConfig;
+ };
+
+ const config = useMemo( () => {
+ return parseContent( content );
+ }, [ content ] );
+
+ const setConfig = useCallback(
+ ( callback ) => {
+ const currentConfig = parseContent(
+ getEditedEntityRecord(
+ 'postType',
+ 'wp_global_styles',
+ globalStylesId
+ )?.content
+ );
+ const updatedConfig = callback( currentConfig );
+ editEntityRecord( 'postType', 'wp_global_styles', globalStylesId, {
+ content: JSON.stringify( {
+ ...updatedConfig,
+ settings: removeUserOriginFromSettings(
+ updatedConfig.settings
+ ),
+ } ),
+ } );
+ },
+ [ globalStylesId ]
+ );
+
+ return [ config, setConfig ];
+}
+
+function useGlobalStylesBaseConfig() {
+ const baseConfig = useSelect( ( select ) => {
+ return select( editSiteStore ).getSettings()
+ .__experimentalGlobalStylesBaseStyles;
+ }, [] );
+
+ return baseConfig;
+}
+
+export function useGlobalStylesConfig() {
+ const [ userConfig, setUserConfig ] = useGlobalStylesUserConfig();
+ const baseConfig = useGlobalStylesBaseConfig();
+ const mergedConfig = useMemo( () => {
+ return mergeBaseAndUserConfigs( baseConfig, userConfig );
+ }, [ userConfig, baseConfig ] );
+
+ return [ baseConfig, userConfig, mergedConfig, setUserConfig ];
+}
+
+export const useGlobalStylesReset = () => {
+ const [ config, setConfig ] = useGlobalStylesUserConfig();
+ const canReset = !! config && ! isEqual( config, EMPTY_CONFIG );
+ return [
+ canReset,
+ useCallback( () => setConfig( () => EMPTY_CONFIG ), [ setConfig ] ),
+ ];
+};
+
+export function useSetting( path, blockName, source = 'all' ) {
+ const [
+ baseConfig,
+ userConfig,
+ mergedConfig,
+ setUserConfig,
+ ] = useGlobalStylesConfig();
+
+ const fullPath = ! blockName
+ ? `settings.${ path }`
+ : `settings.blocks.${ blockName }.${ path }`;
+
+ const setSetting = ( newValue ) => {
+ setUserConfig( ( currentConfig ) => {
+ const newUserConfig = cloneDeep( currentConfig );
+ const pathToSet = PATHS_WITH_MERGE[ path ]
+ ? fullPath + '.user'
+ : fullPath;
+ set( newUserConfig, pathToSet, newValue );
+
+ return newUserConfig;
+ } );
+ };
+
+ const getSettingValueForContext = ( name ) => {
+ const currentPath = ! name
+ ? `settings.${ path }`
+ : `settings.blocks.${ name }.${ path }`;
+
+ const getSettingValue = ( configToUse ) => {
+ const result = get( configToUse, currentPath );
+ if ( PATHS_WITH_MERGE[ path ] ) {
+ return result?.user ?? result?.theme ?? result?.core;
+ }
+ return result;
+ };
+
+ let result;
+ switch ( source ) {
+ case 'all':
+ result = getSettingValue( mergedConfig );
+ break;
+ case 'user':
+ result = getSettingValue( userConfig );
+ break;
+ case 'base':
+ result = getSettingValue( baseConfig );
+ break;
+ default:
+ throw 'Unsupported source';
+ }
+
+ return result;
+ };
+
+ // Unlike styles settings get inherited from top level settings.
+ const resultWithFallback =
+ getSettingValueForContext( blockName ) ?? getSettingValueForContext();
+
+ return [ resultWithFallback, setSetting ];
+}
+
+export function useStyle( path, blockName, source = 'all' ) {
+ const [
+ baseConfig,
+ userConfig,
+ mergedConfig,
+ setUserConfig,
+ ] = useGlobalStylesConfig();
+ const finalPath = ! blockName
+ ? `styles.${ path }`
+ : `styles.blocks.${ blockName }.${ path }`;
+
+ const setStyle = ( newValue ) => {
+ setUserConfig( ( currentConfig ) => {
+ const newUserConfig = cloneDeep( currentConfig );
+ set(
+ newUserConfig,
+ finalPath,
+ getPresetVariableFromValue(
+ mergedConfig.settings,
+ blockName,
+ path,
+ newValue
+ )
+ );
+ return newUserConfig;
+ } );
+ };
+
+ let result;
+ switch ( source ) {
+ case 'all':
+ result = getValueFromVariable(
+ mergedConfig.settings,
+ blockName,
+ get( userConfig, finalPath ) ?? get( baseConfig, finalPath )
+ );
+ break;
+ case 'user':
+ result = getValueFromVariable(
+ mergedConfig.settings,
+ blockName,
+ get( userConfig, finalPath )
+ );
+ break;
+ case 'base':
+ result = getValueFromVariable(
+ baseConfig.settings,
+ blockName,
+ get( baseConfig, finalPath )
+ );
+ break;
+ default:
+ throw 'Unsupported source';
+ }
+
+ return [ result, setStyle ];
+}
+
+const ROOT_BLOCK_SUPPORTS = [
+ 'background',
+ 'backgroundColor',
+ 'color',
+ 'linkColor',
+ 'fontFamily',
+ 'fontSize',
+ 'fontStyle',
+ 'fontWeight',
+ 'lineHeight',
+ 'textDecoration',
+ 'textTransform',
+ 'padding',
+];
+
+export function getSupportedGlobalStylesPanels( name ) {
+ if ( ! name ) {
+ return ROOT_BLOCK_SUPPORTS;
+ }
+
+ const blockType = getBlockType( name );
+
+ if ( ! blockType ) {
+ return [];
+ }
+
+ const supportKeys = [];
+ Object.keys( STYLE_PROPERTY ).forEach( ( styleName ) => {
+ if ( ! STYLE_PROPERTY[ styleName ].support ) {
+ return;
+ }
+
+ // Opting out means that, for certain support keys like background color,
+ // blocks have to explicitly set the support value false. If the key is
+ // unset, we still enable it.
+ if ( STYLE_PROPERTY[ styleName ].requiresOptOut ) {
+ if (
+ has(
+ blockType.supports,
+ STYLE_PROPERTY[ styleName ].support[ 0 ]
+ ) &&
+ get(
+ blockType.supports,
+ STYLE_PROPERTY[ styleName ].support
+ ) !== false
+ ) {
+ return supportKeys.push( styleName );
+ }
+ }
+
+ if (
+ get(
+ blockType.supports,
+ STYLE_PROPERTY[ styleName ].support,
+ false
+ )
+ ) {
+ return supportKeys.push( styleName );
+ }
+ } );
+
+ return supportKeys;
+}
diff --git a/packages/edit-site/src/components/global-styles/index.js b/packages/edit-site/src/components/global-styles/index.js
index e062747a8d2118..026a4fd759f325 100644
--- a/packages/edit-site/src/components/global-styles/index.js
+++ b/packages/edit-site/src/components/global-styles/index.js
@@ -1,81 +1,3 @@
-/**
- * External dependencies
- */
-import { map } from 'lodash';
-
-/**
- * WordPress dependencies
- */
-import {
- __experimentalNavigatorProvider as NavigatorProvider,
- __experimentalNavigatorScreen as NavigatorScreen,
-} from '@wordpress/components';
-
-/**
- * Internal dependencies
- */
-import { useGlobalStylesContext } from '../editor/global-styles-provider';
-import ScreenRoot from './screen-root';
-import ScreenBlockList from './screen-block-list';
-import ScreenBlock from './screen-block';
-import ScreenTypography from './screen-typography';
-import ScreenColors from './screen-colors';
-import ScreenColorPalette from './screen-color-palette';
-import ScreenLayout from './screen-layout';
-
-function ContextScreens( { name } ) {
- const parentMenu = name === undefined ? '' : '/blocks/' + name;
-
- return (
- <>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- >
- );
-}
-
-function GlobalStyles() {
- const { blocks } = useGlobalStylesContext();
-
- return (
-
-
-
-
-
-
-
-
-
- { map( blocks, ( block, name ) => (
-
-
-
- ) ) }
-
-
-
- { map( blocks, ( _, name ) => (
-
- ) ) }
-
- );
-}
-
-export default GlobalStyles;
+export { default as GlobalStylesUI } from './ui';
+export { useGlobalStylesReset, useStyle, useSetting } from './hooks';
+export { useGlobalStylesOutput } from './use-global-styles-output';
diff --git a/packages/edit-site/src/components/global-styles/palette.js b/packages/edit-site/src/components/global-styles/palette.js
index 0b30d6284c00b9..021392fcc5ab6f 100644
--- a/packages/edit-site/src/components/global-styles/palette.js
+++ b/packages/edit-site/src/components/global-styles/palette.js
@@ -16,15 +16,14 @@ import { __, _n, sprintf } from '@wordpress/i18n';
* Internal dependencies
*/
import Subtitle from './subtitle';
-import { useSetting } from '../editor/utils';
import NavigationButton from './navigation-button';
+import { useSetting } from './hooks';
-function Palette( { contextName } ) {
- const colors = useSetting( 'color.palette', contextName );
- const screenPath =
- contextName === 'root'
- ? '/colors/palette'
- : '/blocks/' + contextName + '/colors/palette';
+function Palette( { name } ) {
+ const [ colors ] = useSetting( 'color.palette', name );
+ const screenPath = ! name
+ ? '/colors/palette'
+ : '/blocks/' + name + '/colors/palette';
return (
diff --git a/packages/edit-site/src/components/global-styles/preview.js b/packages/edit-site/src/components/global-styles/preview.js
index 387f3d30ec0e27..c691e1134fe901 100644
--- a/packages/edit-site/src/components/global-styles/preview.js
+++ b/packages/edit-site/src/components/global-styles/preview.js
@@ -10,14 +10,14 @@ import {
/**
* Internal dependencies
- */ import { useGlobalStylesContext } from '../editor/global-styles-provider';
+ */
+import { useStyle } from './hooks';
const StylesPreview = () => {
- const { getStyle } = useGlobalStylesContext();
- const fontFamily = getStyle( 'root', 'fontFamily' ) ?? 'serif';
- const textColor = getStyle( 'root', 'color' ) ?? 'black';
- const linkColor = getStyle( 'root', 'linkColor' ) ?? 'blue';
- const backgroundColor = getStyle( 'root', 'backgroundColor' ) ?? 'white';
+ const [ fontFamily = 'serif' ] = useStyle( 'typography.fontFamily' );
+ const [ textColor = 'black' ] = useStyle( 'color.text' );
+ const [ linkColor = 'blue' ] = useStyle( 'elements.link.color.text' );
+ const [ backgroundColor = 'white' ] = useStyle( 'color.background' );
return (
{
style={ { background: backgroundColor } }
>
-
- A
- a
-
+ Aa
diff --git a/packages/edit-site/src/components/global-styles/screen-block-list.js b/packages/edit-site/src/components/global-styles/screen-block-list.js
index d673226b5a8d06..159d2736506750 100644
--- a/packages/edit-site/src/components/global-styles/screen-block-list.js
+++ b/packages/edit-site/src/components/global-styles/screen-block-list.js
@@ -1,13 +1,12 @@
/**
* WordPress dependencies
*/
-import { getBlockType } from '@wordpress/blocks';
+import { getBlockTypes } from '@wordpress/blocks';
import { __ } from '@wordpress/i18n';
/**
* Internal dependencies
*/
-import { useGlobalStylesContext } from '../editor/global-styles-provider';
import { useHasBorderPanel } from './border-panel';
import { useHasColorPanel } from './color-panel';
import { useHasDimensionsPanel } from './dimensions-panel';
@@ -16,12 +15,10 @@ import ScreenHeader from './header';
import NavigationButton from './navigation-button';
function BlockMenuItem( { block } ) {
- const { blocks } = useGlobalStylesContext();
- const context = blocks[ block.name ];
- const hasTypographyPanel = useHasTypographyPanel( context );
- const hasColorPanel = useHasColorPanel( context );
- const hasBorderPanel = useHasBorderPanel( context );
- const hasDimensionsPanel = useHasDimensionsPanel( context );
+ const hasTypographyPanel = useHasTypographyPanel( block.name );
+ const hasColorPanel = useHasColorPanel( block.name );
+ const hasBorderPanel = useHasBorderPanel( block.name );
+ const hasDimensionsPanel = useHasDimensionsPanel( block.name );
const hasLayoutPanel = hasBorderPanel || hasDimensionsPanel;
const hasBlockMenuItem =
hasTypographyPanel || hasColorPanel || hasLayoutPanel;
@@ -38,15 +35,10 @@ function BlockMenuItem( { block } ) {
}
function ScreenBlockList() {
- const { blocks } = useGlobalStylesContext();
- const visibleBlocks = Object.keys( blocks )
- .map( ( name ) => getBlockType( name ) )
- .filter( ( blockType ) => !! blockType );
-
return (
<>
- { visibleBlocks.map( ( block ) => (
+ { getBlockTypes().map( ( block ) => (
-
+
>
);
}
diff --git a/packages/edit-site/src/components/global-styles/screen-color-palette.js b/packages/edit-site/src/components/global-styles/screen-color-palette.js
index a2518816b53f8e..e207b76645ef3e 100644
--- a/packages/edit-site/src/components/global-styles/screen-color-palette.js
+++ b/packages/edit-site/src/components/global-styles/screen-color-palette.js
@@ -6,12 +6,10 @@ import { __ } from '@wordpress/i18n';
/**
* Internal dependencies
*/
-import { useGlobalStylesContext } from '../editor/global-styles-provider';
import ColorPalettePanel from './color-palette-panel';
import ScreenHeader from './header';
function ScreenColorPalette( { name } ) {
- const { getSetting, setSetting } = useGlobalStylesContext();
const parentMenu = name === undefined ? '' : '/blocks/' + name;
return (
@@ -21,11 +19,7 @@ function ScreenColorPalette( { name } ) {
title={ __( 'Color Palette' ) }
description={ __( 'Manage the color palette of your site' ) }
/>
-
+
>
);
}
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 abb6f8152d11ad..5e4e29b7dd3ef8 100644
--- a/packages/edit-site/src/components/global-styles/screen-colors.js
+++ b/packages/edit-site/src/components/global-styles/screen-colors.js
@@ -6,13 +6,10 @@ import { __ } from '@wordpress/i18n';
/**
* Internal dependencies
*/
-import { useGlobalStylesContext } from '../editor/global-styles-provider';
import ColorPanel from './color-panel';
import ScreenHeader from './header';
function ScreenColors( { name } ) {
- const { root, blocks, getStyle, setStyle } = useGlobalStylesContext();
- const context = name === undefined ? root : blocks[ name ];
const parentMenu = name === undefined ? '' : '/blocks/' + name;
return (
@@ -24,11 +21,7 @@ function ScreenColors( { name } ) {
'Manage the color palette and how it applies to the elements of your site'
) }
/>
-
+
>
);
}
diff --git a/packages/edit-site/src/components/global-styles/screen-layout.js b/packages/edit-site/src/components/global-styles/screen-layout.js
index b1a8dc449ac1e4..36d9b2444ee67c 100644
--- a/packages/edit-site/src/components/global-styles/screen-layout.js
+++ b/packages/edit-site/src/components/global-styles/screen-layout.js
@@ -6,17 +6,14 @@ import { __ } from '@wordpress/i18n';
/**
* Internal dependencies
*/
-import { useGlobalStylesContext } from '../editor/global-styles-provider';
import BorderPanel, { useHasBorderPanel } from './border-panel';
import DimensionsPanel, { useHasDimensionsPanel } from './dimensions-panel';
import ScreenHeader from './header';
function ScreenLayout( { name } ) {
- const { root, blocks, getStyle, setStyle } = useGlobalStylesContext();
- const context = name === undefined ? root : blocks[ name ];
const parentMenu = name === undefined ? '' : '/blocks/' + name;
- const hasBorderPanel = useHasBorderPanel( context );
- const hasDimensionsPanel = useHasDimensionsPanel( context );
+ const hasBorderPanel = useHasBorderPanel( name );
+ const hasDimensionsPanel = useHasDimensionsPanel( name );
return (
<>
@@ -24,20 +21,8 @@ function ScreenLayout( { name } ) {
back={ parentMenu ? parentMenu : '/' }
title={ __( 'Layout' ) }
/>
- { hasDimensionsPanel && (
-
- ) }
- { hasBorderPanel && (
-
- ) }
+ { hasDimensionsPanel && }
+ { hasBorderPanel && }
>
);
}
diff --git a/packages/edit-site/src/components/global-styles/screen-root.js b/packages/edit-site/src/components/global-styles/screen-root.js
index 20e0961825f450..2f22e6f4d3b656 100644
--- a/packages/edit-site/src/components/global-styles/screen-root.js
+++ b/packages/edit-site/src/components/global-styles/screen-root.js
@@ -12,17 +12,14 @@ import { __ } from '@wordpress/i18n';
*/
import StylesPreview from './preview';
import NavigationButton from './navigation-button';
-import { useGlobalStylesContext } from '../editor/global-styles-provider';
import ContextMenu from './context-menu';
function ScreenRoot() {
- const { root } = useGlobalStylesContext();
-
return (
<>
-
+
-
diff --git a/packages/edit-site/src/components/global-styles/screen-typography.js b/packages/edit-site/src/components/global-styles/screen-typography.js
index edf8cfe9b53fbe..56c1eee11540ad 100644
--- a/packages/edit-site/src/components/global-styles/screen-typography.js
+++ b/packages/edit-site/src/components/global-styles/screen-typography.js
@@ -6,13 +6,10 @@ import { __ } from '@wordpress/i18n';
/**
* Internal dependencies
*/
-import { useGlobalStylesContext } from '../editor/global-styles-provider';
import TypographyPanel from './typography-panel';
import ScreenHeader from './header';
function ScreenTypography( { name } ) {
- const { root, blocks, getStyle, setStyle } = useGlobalStylesContext();
- const context = name === undefined ? root : blocks[ name ];
const parentMenu = name === undefined ? '' : '/blocks/' + name;
return (
@@ -21,11 +18,7 @@ function ScreenTypography( { name } ) {
back={ parentMenu ? parentMenu : '/' }
title={ __( 'Typography' ) }
/>
-
+
>
);
}
diff --git a/packages/edit-site/src/components/editor/test/global-styles-renderer.js b/packages/edit-site/src/components/global-styles/test/use-global-styles-output.js
similarity index 96%
rename from packages/edit-site/src/components/editor/test/global-styles-renderer.js
rename to packages/edit-site/src/components/global-styles/test/use-global-styles-output.js
index 24e862da6ee201..b5449b93a074fd 100644
--- a/packages/edit-site/src/components/editor/test/global-styles-renderer.js
+++ b/packages/edit-site/src/components/global-styles/test/use-global-styles-output.js
@@ -11,7 +11,7 @@ import {
getNodesWithStyles,
toCustomProperties,
toStyles,
-} from '../global-styles-renderer';
+} from '../use-global-styles-output';
import { ROOT_BLOCK_SELECTOR } from '../utils';
describe( 'global styles renderer', () => {
@@ -55,16 +55,7 @@ describe( 'global styles renderer', () => {
};
const blockSelectors = {
'core/heading': {
- selector: 'h1,h2,h3,h4,h5,h6',
- elements: {
- link: 'h1 a,h2 a,h3 a,h4 a,h5 a,h6 a',
- h1: 'h1',
- h2: 'h2',
- h3: 'h3',
- h4: 'h4',
- h5: 'h5',
- h6: 'h6',
- },
+ selector: '.my-heading1, .my-heading2',
},
};
expect( getNodesWithStyles( tree, blockSelectors ) ).toEqual( [
@@ -93,7 +84,7 @@ describe( 'global styles renderer', () => {
text: 'blue',
},
},
- selector: 'h1,h2,h3,h4,h5,h6',
+ selector: '.my-heading1, .my-heading2',
},
{
styles: {
@@ -101,7 +92,7 @@ describe( 'global styles renderer', () => {
fontSize: '42px',
},
},
- selector: 'h1',
+ selector: '.my-heading1 h1, .my-heading2 h1',
},
{
styles: {
@@ -109,7 +100,7 @@ describe( 'global styles renderer', () => {
fontSize: '23px',
},
},
- selector: 'h2',
+ selector: '.my-heading1 h2, .my-heading2 h2',
},
] );
} );
diff --git a/packages/edit-site/src/components/editor/test/utils.js b/packages/edit-site/src/components/global-styles/test/utils.js
similarity index 84%
rename from packages/edit-site/src/components/editor/test/utils.js
rename to packages/edit-site/src/components/global-styles/test/utils.js
index 0f067424687d5b..40e434a935dbb5 100644
--- a/packages/edit-site/src/components/editor/test/utils.js
+++ b/packages/edit-site/src/components/global-styles/test/utils.js
@@ -1,7 +1,7 @@
/**
* Internal dependencies
*/
-import { getPresetVariable, getValueFromVariable } from '../utils';
+import { getPresetVariableFromValue, getValueFromVariable } from '../utils';
describe( 'editor utils', () => {
const styles = {
@@ -49,15 +49,15 @@ describe( 'editor utils', () => {
isGlobalStylesUserThemeJSON: true,
};
- describe( 'getPresetVariable', () => {
+ describe( 'getPresetVariableFromValue', () => {
const context = 'root';
- const propertyName = 'color';
+ const propertyName = 'color.text';
const value = '#007cba';
describe( 'when a provided global style (e.g. fontFamily, color,etc.) does not exist', () => {
it( 'returns the originally provided value', () => {
- const actual = getPresetVariable(
- styles,
+ const actual = getPresetVariableFromValue(
+ styles.settings,
context,
'fakePropertyName',
value
@@ -68,8 +68,8 @@ describe( 'editor utils', () => {
describe( 'when a global style is cleared by the user', () => {
it( 'returns an undefined preset variable', () => {
- const actual = getPresetVariable(
- styles,
+ const actual = getPresetVariableFromValue(
+ styles.settings,
context,
propertyName,
undefined
@@ -82,8 +82,8 @@ describe( 'editor utils', () => {
describe( 'and it is not a preset value (e.g. custom color)', () => {
it( 'returns the originally provided value', () => {
const customValue = '#6e4545';
- const actual = getPresetVariable(
- styles,
+ const actual = getPresetVariableFromValue(
+ styles.settings,
context,
propertyName,
customValue
@@ -94,8 +94,8 @@ describe( 'editor utils', () => {
describe( 'and it is a preset value', () => {
it( 'returns the preset variable', () => {
- const actual = getPresetVariable(
- styles,
+ const actual = getPresetVariableFromValue(
+ styles.settings,
context,
propertyName,
value
@@ -110,7 +110,7 @@ describe( 'editor utils', () => {
describe( 'when provided an invalid variable', () => {
it( 'returns the originally provided value', () => {
const actual = getValueFromVariable(
- styles,
+ styles.settings,
'root',
undefined
);
@@ -122,7 +122,7 @@ describe( 'editor utils', () => {
describe( 'when provided a preset variable', () => {
it( 'retrieves the correct preset value', () => {
const actual = getValueFromVariable(
- styles,
+ styles.settings,
'root',
'var:preset|color|primary'
);
@@ -134,7 +134,7 @@ describe( 'editor utils', () => {
describe( 'when provided a custom variable', () => {
it( 'retrieves the correct custom value', () => {
const actual = getValueFromVariable(
- styles,
+ styles.settings,
'root',
'var(--wp--custom--color--secondary)'
);
diff --git a/packages/edit-site/src/components/global-styles/typography-panel.js b/packages/edit-site/src/components/global-styles/typography-panel.js
index df44837a38cc6f..723d2fee1c2298 100644
--- a/packages/edit-site/src/components/global-styles/typography-panel.js
+++ b/packages/edit-site/src/components/global-styles/typography-panel.js
@@ -12,12 +12,13 @@ import { PanelBody, FontSizePicker } from '@wordpress/components';
/**
* Internal dependencies
*/
-import { useSetting } from '../editor/utils';
+import { getSupportedGlobalStylesPanels, useSetting, useStyle } from './hooks';
-export function useHasTypographyPanel( { supports, name } ) {
- const hasLineHeight = useHasLineHeightControl( { supports, name } );
- const hasFontAppearance = useHasAppearanceControl( { supports, name } );
- const hasLetterSpacing = useHasLetterSpacingControl( { supports, name } );
+export function useHasTypographyPanel( name ) {
+ const hasLineHeight = useHasLineHeightControl( name );
+ const hasFontAppearance = useHasAppearanceControl( name );
+ const hasLetterSpacing = useHasLetterSpacingControl( name );
+ const supports = getSupportedGlobalStylesPanels( name );
return (
hasLineHeight ||
hasFontAppearance ||
@@ -26,92 +27,109 @@ export function useHasTypographyPanel( { supports, name } ) {
);
}
-function useHasLineHeightControl( { supports, name } ) {
+function useHasLineHeightControl( name ) {
+ const supports = getSupportedGlobalStylesPanels( name );
return (
- useSetting( 'typography.customLineHeight', name ) &&
+ useSetting( 'typography.customLineHeight', name )[ 0 ] &&
supports.includes( 'lineHeight' )
);
}
-function useHasAppearanceControl( { supports, name } ) {
+function useHasAppearanceControl( name ) {
+ const supports = getSupportedGlobalStylesPanels( name );
const hasFontStyles =
- useSetting( 'typography.customFontStyle', name ) &&
+ useSetting( 'typography.customFontStyle', name )[ 0 ] &&
supports.includes( 'fontStyle' );
const hasFontWeights =
- useSetting( 'typography.customFontWeight', name ) &&
+ useSetting( 'typography.customFontWeight', name )[ 0 ] &&
supports.includes( 'fontWeight' );
return hasFontStyles || hasFontWeights;
}
-function useHasLetterSpacingControl( { supports, name } ) {
+function useHasLetterSpacingControl( name ) {
+ const supports = getSupportedGlobalStylesPanels( name );
return (
- useSetting( 'typography.customLetterSpacing', name ) &&
+ useSetting( 'typography.customLetterSpacing', name )[ 0 ] &&
supports.includes( 'letterSpacing' )
);
}
-export default function TypographyPanel( {
- context: { supports, name },
- getStyle,
- setStyle,
-} ) {
- const fontSizes = useSetting( 'typography.fontSizes', name );
+export default function TypographyPanel( { name } ) {
+ const supports = getSupportedGlobalStylesPanels( name );
+ const [ fontSizes ] = useSetting( 'typography.fontSizes', name );
const disableCustomFontSizes = ! useSetting(
'typography.customFontSize',
name
- );
- const fontFamilies = useSetting( 'typography.fontFamilies', name );
+ )[ 0 ];
+ const [ fontFamilies ] = useSetting( 'typography.fontFamilies', name );
const hasFontStyles =
- useSetting( 'typography.customFontStyle', name ) &&
+ useSetting( 'typography.customFontStyle', name )[ 0 ] &&
supports.includes( 'fontStyle' );
const hasFontWeights =
- useSetting( 'typography.customFontWeight', name ) &&
+ useSetting( 'typography.customFontWeight', name )[ 0 ] &&
supports.includes( 'fontWeight' );
- const hasLineHeightEnabled = useHasLineHeightControl( { supports, name } );
- const hasAppearanceControl = useHasAppearanceControl( { supports, name } );
- const hasLetterSpacingControl = useHasLetterSpacingControl( {
- supports,
- name,
- } );
+ const hasLineHeightEnabled = useHasLineHeightControl( name );
+ const hasAppearanceControl = useHasAppearanceControl( name );
+ const hasLetterSpacingControl = useHasLetterSpacingControl( name );
+
+ const [ fontFamily, setFontFamily ] = useStyle(
+ 'typography.fontFamily',
+ name
+ );
+ const [ fontSize, setFontSize ] = useStyle( 'typography.fontSize', name );
+
+ const [ fontStyle, setFontStyle ] = useStyle(
+ 'typography.fontStyle',
+ name
+ );
+ const [ fontWeight, setFontWeight ] = useStyle(
+ 'typography.fontWeight',
+ name
+ );
+ const [ lineHeight, setLineHeight ] = useStyle(
+ 'typography.lineHeight',
+ name
+ );
+ const [ letterSpacing, setLetterSpacing ] = useStyle(
+ 'typography.letterSpacing',
+ name
+ );
return (
{ supports.includes( 'fontFamily' ) && (
- setStyle( name, 'fontFamily', value )
- }
+ value={ fontFamily }
+ onChange={ setFontFamily }
/>
) }
{ supports.includes( 'fontSize' ) && (
- setStyle( name, 'fontSize', value )
- }
+ value={ fontSize }
+ onChange={ setFontSize }
fontSizes={ fontSizes }
disableCustomFontSizes={ disableCustomFontSizes }
/>
) }
{ hasLineHeightEnabled && (
- setStyle( name, 'lineHeight', value )
- }
+ value={ lineHeight }
+ onChange={ setLineHeight }
/>
) }
{ hasAppearanceControl && (
{
- setStyle( name, 'fontStyle', fontStyle );
- setStyle( name, 'fontWeight', fontWeight );
+ onChange={ ( {
+ fontStyle: newFontStyle,
+ fontWeight: newFontWeight,
+ } ) => {
+ setFontStyle( newFontStyle );
+ setFontWeight( newFontWeight );
} }
hasFontStyles={ hasFontStyles }
hasFontWeights={ hasFontWeights }
@@ -119,10 +137,8 @@ export default function TypographyPanel( {
) }
{ hasLetterSpacingControl && (
- setStyle( name, 'letterSpacing', value )
- }
+ value={ letterSpacing }
+ onChange={ setLetterSpacing }
/>
) }
diff --git a/packages/edit-site/src/components/global-styles/ui.js b/packages/edit-site/src/components/global-styles/ui.js
new file mode 100644
index 00000000000000..180bf343dd1206
--- /dev/null
+++ b/packages/edit-site/src/components/global-styles/ui.js
@@ -0,0 +1,79 @@
+/**
+ * WordPress dependencies
+ */
+import {
+ __experimentalNavigatorProvider as NavigatorProvider,
+ __experimentalNavigatorScreen as NavigatorScreen,
+} from '@wordpress/components';
+import { getBlockTypes } from '@wordpress/blocks';
+
+/**
+ * Internal dependencies
+ */
+import ScreenRoot from './screen-root';
+import ScreenBlockList from './screen-block-list';
+import ScreenBlock from './screen-block';
+import ScreenTypography from './screen-typography';
+import ScreenColors from './screen-colors';
+import ScreenColorPalette from './screen-color-palette';
+import ScreenLayout from './screen-layout';
+
+function ContextScreens( { name } ) {
+ const parentMenu = name === undefined ? '' : '/blocks/' + name;
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+}
+
+function GlobalStylesUI() {
+ const blocks = getBlockTypes();
+
+ return (
+
+
+
+
+
+
+
+
+
+ { blocks.map( ( block ) => (
+
+
+
+ ) ) }
+
+
+
+ { blocks.map( ( block ) => (
+
+ ) ) }
+
+ );
+}
+
+export default GlobalStylesUI;
diff --git a/packages/edit-site/src/components/global-styles/use-global-styles-output.js b/packages/edit-site/src/components/global-styles/use-global-styles-output.js
new file mode 100644
index 00000000000000..69e5bf5879ddd3
--- /dev/null
+++ b/packages/edit-site/src/components/global-styles/use-global-styles-output.js
@@ -0,0 +1,392 @@
+/**
+ * External dependencies
+ */
+import {
+ first,
+ forEach,
+ get,
+ isEmpty,
+ isString,
+ kebabCase,
+ pickBy,
+ reduce,
+ set,
+ startsWith,
+} from 'lodash';
+
+/**
+ * WordPress dependencies
+ */
+import {
+ __EXPERIMENTAL_STYLE_PROPERTY as STYLE_PROPERTY,
+ __EXPERIMENTAL_ELEMENTS as ELEMENTS,
+ getBlockTypes,
+} from '@wordpress/blocks';
+import { useEffect, useState } from '@wordpress/element';
+
+/**
+ * Internal dependencies
+ */
+import { useGlobalStylesConfig } from './hooks';
+
+/**
+ * Internal dependencies
+ */
+import { PRESET_METADATA, ROOT_BLOCK_SELECTOR } from './utils';
+
+function compileStyleValue( uncompiledValue ) {
+ const VARIABLE_REFERENCE_PREFIX = 'var:';
+ const VARIABLE_PATH_SEPARATOR_TOKEN_ATTRIBUTE = '|';
+ const VARIABLE_PATH_SEPARATOR_TOKEN_STYLE = '--';
+ if ( startsWith( uncompiledValue, VARIABLE_REFERENCE_PREFIX ) ) {
+ const variable = uncompiledValue
+ .slice( VARIABLE_REFERENCE_PREFIX.length )
+ .split( VARIABLE_PATH_SEPARATOR_TOKEN_ATTRIBUTE )
+ .join( VARIABLE_PATH_SEPARATOR_TOKEN_STYLE );
+ return `var(--wp--${ variable })`;
+ }
+ return uncompiledValue;
+}
+
+/**
+ * Transform given preset tree into a set of style declarations.
+ *
+ * @param {Object} blockPresets
+ *
+ * @return {Array} An array of style declarations.
+ */
+function getPresetsDeclarations( blockPresets = {} ) {
+ return reduce(
+ PRESET_METADATA,
+ ( declarations, { path, valueKey, cssVarInfix } ) => {
+ const presetByOrigin = get( blockPresets, path, [] );
+ [ 'core', 'theme', 'user' ].forEach( ( origin ) => {
+ if ( presetByOrigin[ origin ] ) {
+ presetByOrigin[ origin ].forEach( ( value ) => {
+ declarations.push(
+ `--wp--preset--${ cssVarInfix }--${ kebabCase(
+ value.slug
+ ) }: ${ value[ valueKey ] }`
+ );
+ } );
+ }
+ } );
+
+ return declarations;
+ },
+ []
+ );
+}
+
+/**
+ * Transform given preset tree into a set of preset class declarations.
+ *
+ * @param {string} blockSelector
+ * @param {Object} blockPresets
+ * @return {string} CSS declarations for the preset classes.
+ */
+function getPresetsClasses( blockSelector, blockPresets = {} ) {
+ return reduce(
+ PRESET_METADATA,
+ ( declarations, { path, cssVarInfix, classes } ) => {
+ if ( ! classes ) {
+ return declarations;
+ }
+
+ const presetByOrigin = get( blockPresets, path, [] );
+ [ 'core', 'theme', 'user' ].forEach( ( origin ) => {
+ if ( presetByOrigin[ origin ] ) {
+ presetByOrigin[ origin ].forEach( ( { slug } ) => {
+ classes.forEach( ( { classSuffix, propertyName } ) => {
+ const classSelectorToUse = `.has-${ kebabCase(
+ slug
+ ) }-${ classSuffix }`;
+ const selectorToUse = blockSelector
+ .split( ',' ) // Selector can be "h1, h2, h3"
+ .map(
+ ( selector ) =>
+ `${ selector }${ classSelectorToUse }`
+ )
+ .join( ',' );
+ const value = `var(--wp--preset--${ cssVarInfix }--${ kebabCase(
+ slug
+ ) })`;
+ declarations += `${ selectorToUse }{${ propertyName }: ${ value } !important;}`;
+ } );
+ } );
+ }
+ } );
+ return declarations;
+ },
+ ''
+ );
+}
+
+function flattenTree( input = {}, prefix, token ) {
+ let result = [];
+ Object.keys( input ).forEach( ( key ) => {
+ const newKey = prefix + kebabCase( key.replace( '/', '-' ) );
+ const newLeaf = input[ key ];
+
+ if ( newLeaf instanceof Object ) {
+ const newPrefix = newKey + token;
+ result = [ ...result, ...flattenTree( newLeaf, newPrefix, token ) ];
+ } else {
+ result.push( `${ newKey }: ${ newLeaf }` );
+ }
+ } );
+ return result;
+}
+
+/**
+ * Transform given style tree into a set of style declarations.
+ *
+ * @param {Object} blockStyles Block styles.
+ *
+ * @return {Array} An array of style declarations.
+ */
+function getStylesDeclarations( blockStyles = {} ) {
+ return reduce(
+ STYLE_PROPERTY,
+ ( declarations, { value, properties }, key ) => {
+ const pathToValue = value;
+ if ( first( pathToValue ) === 'elements' ) {
+ return declarations;
+ }
+
+ const styleValue = get( blockStyles, pathToValue );
+
+ if ( !! properties && ! isString( styleValue ) ) {
+ Object.entries( properties ).forEach( ( entry ) => {
+ const [ name, prop ] = entry;
+
+ if ( ! get( styleValue, [ prop ], false ) ) {
+ // Do not create a declaration
+ // for sub-properties that don't have any value.
+ return;
+ }
+
+ const cssProperty = kebabCase( name );
+ declarations.push(
+ `${ cssProperty }: ${ compileStyleValue(
+ get( styleValue, [ prop ] )
+ ) }`
+ );
+ } );
+ } else if ( get( blockStyles, pathToValue, false ) ) {
+ const cssProperty = key.startsWith( '--' )
+ ? key
+ : kebabCase( key );
+ declarations.push(
+ `${ cssProperty }: ${ compileStyleValue(
+ get( blockStyles, pathToValue )
+ ) }`
+ );
+ }
+
+ return declarations;
+ },
+ []
+ );
+}
+
+export const getNodesWithStyles = ( tree, blockSelectors ) => {
+ const nodes = [];
+
+ if ( ! tree?.styles ) {
+ return nodes;
+ }
+
+ const pickStyleKeys = ( treeToPickFrom ) =>
+ pickBy( treeToPickFrom, ( value, key ) =>
+ [ 'border', 'color', 'spacing', 'typography' ].includes( key )
+ );
+
+ // Top-level.
+ const styles = pickStyleKeys( tree.styles );
+ if ( !! styles ) {
+ nodes.push( {
+ styles,
+ selector: ROOT_BLOCK_SELECTOR,
+ } );
+ }
+ forEach( tree.styles?.elements, ( value, key ) => {
+ if ( !! value && !! ELEMENTS[ key ] ) {
+ nodes.push( {
+ styles: value,
+ selector: ELEMENTS[ key ],
+ } );
+ }
+ } );
+
+ // Iterate over blocks: they can have styles & elements.
+ forEach( tree.styles?.blocks, ( node, blockName ) => {
+ const blockStyles = pickStyleKeys( node );
+ if ( !! blockStyles && !! blockSelectors?.[ blockName ]?.selector ) {
+ nodes.push( {
+ styles: blockStyles,
+ selector: blockSelectors[ blockName ].selector,
+ } );
+ }
+
+ forEach( node?.elements, ( value, elementName ) => {
+ if (
+ !! value &&
+ !! blockSelectors?.[ blockName ] &&
+ !! ELEMENTS?.[ elementName ]
+ ) {
+ nodes.push( {
+ styles: value,
+ selector: blockSelectors[ blockName ].selector
+ .split( ',' )
+ .map( ( sel ) => sel + ' ' + ELEMENTS[ elementName ] )
+ .join( ',' ),
+ } );
+ }
+ } );
+ } );
+
+ return nodes;
+};
+
+export const getNodesWithSettings = ( tree, blockSelectors ) => {
+ const nodes = [];
+
+ if ( ! tree?.settings ) {
+ return nodes;
+ }
+
+ const pickPresets = ( treeToPickFrom ) => {
+ const presets = {};
+ PRESET_METADATA.forEach( ( { path } ) => {
+ const value = get( treeToPickFrom, path, false );
+ if ( value !== false ) {
+ set( presets, path, value );
+ }
+ } );
+ return presets;
+ };
+
+ // Top-level.
+ const presets = pickPresets( tree.settings );
+ const custom = tree.settings?.custom;
+ if ( ! isEmpty( presets ) || !! custom ) {
+ nodes.push( {
+ presets,
+ custom,
+ selector: ROOT_BLOCK_SELECTOR,
+ } );
+ }
+
+ // Blocks.
+ forEach( tree.settings?.blocks, ( node, blockName ) => {
+ const blockPresets = pickPresets( node );
+ const blockCustom = node.custom;
+ if ( ! isEmpty( blockPresets ) || !! blockCustom ) {
+ nodes.push( {
+ presets: blockPresets,
+ custom: blockCustom,
+ selector: blockSelectors[ blockName ].selector,
+ } );
+ }
+ } );
+
+ return nodes;
+};
+
+export const toCustomProperties = ( tree, blockSelectors ) => {
+ const settings = getNodesWithSettings( tree, blockSelectors );
+
+ let ruleset = '';
+ settings.forEach( ( { presets, custom, selector } ) => {
+ const declarations = getPresetsDeclarations( presets );
+ const customProps = flattenTree( custom, '--wp--custom--', '--' );
+ if ( customProps.length > 0 ) {
+ declarations.push( ...customProps );
+ }
+
+ if ( declarations.length > 0 ) {
+ ruleset = ruleset + `${ selector }{${ declarations.join( ';' ) };}`;
+ }
+ } );
+
+ return ruleset;
+};
+
+export const toStyles = ( tree, blockSelectors ) => {
+ const nodesWithStyles = getNodesWithStyles( tree, blockSelectors );
+ const nodesWithSettings = getNodesWithSettings( tree, blockSelectors );
+
+ let ruleset =
+ '.wp-site-blocks > * + * { margin-top: var( --wp--style--block-gap ); margin-bottom: 0; }';
+ nodesWithStyles.forEach( ( { selector, styles } ) => {
+ const declarations = getStylesDeclarations( styles );
+ if ( declarations.length === 0 ) {
+ return;
+ }
+ ruleset = ruleset + `${ selector }{${ declarations.join( ';' ) };}`;
+ } );
+
+ nodesWithSettings.forEach( ( { selector, presets } ) => {
+ if ( ROOT_BLOCK_SELECTOR === selector ) {
+ // Do not add extra specificity for top-level classes.
+ selector = '';
+ }
+
+ const classes = getPresetsClasses( selector, presets );
+ if ( ! isEmpty( classes ) ) {
+ ruleset = ruleset + classes;
+ }
+ } );
+
+ return ruleset;
+};
+
+const getBlockSelectors = ( blockTypes ) => {
+ const result = {};
+ blockTypes.forEach( ( blockType ) => {
+ const name = blockType.name;
+ const selector =
+ blockType?.supports?.__experimentalSelector ??
+ '.wp-block-' + name.replace( 'core/', '' ).replace( '/', '-' );
+ result[ name ] = {
+ name,
+ selector,
+ };
+ } );
+
+ return result;
+};
+
+export function useGlobalStylesOutput() {
+ const [ stylesheets, setStylesheets ] = useState( [] );
+ const [ settings, setSettings ] = useState( {} );
+ const [ , , mergedConfig ] = useGlobalStylesConfig();
+
+ useEffect( () => {
+ if ( ! mergedConfig?.styles || ! mergedConfig?.settings ) {
+ return;
+ }
+
+ const blockSelectors = getBlockSelectors( getBlockTypes() );
+ const customProperties = toCustomProperties(
+ mergedConfig,
+ blockSelectors
+ );
+ const globalStyles = toStyles( mergedConfig, blockSelectors );
+ setStylesheets( [
+ {
+ css: customProperties,
+ isGlobalStyles: true,
+ __experimentalNoWrapper: true,
+ },
+ {
+ css: globalStyles,
+ isGlobalStyles: true,
+ },
+ ] );
+ setSettings( mergedConfig.settings );
+ }, [ mergedConfig ] );
+
+ return [ stylesheets, settings ];
+}
diff --git a/packages/edit-site/src/components/editor/utils.js b/packages/edit-site/src/components/global-styles/utils.js
similarity index 53%
rename from packages/edit-site/src/components/editor/utils.js
rename to packages/edit-site/src/components/global-styles/utils.js
index aa3a30da0218fa..277758e9c5af41 100644
--- a/packages/edit-site/src/components/editor/utils.js
+++ b/packages/edit-site/src/components/global-styles/utils.js
@@ -1,16 +1,7 @@
/**
* External dependencies
*/
-import { get, find, forEach, camelCase, isString } from 'lodash';
-/**
- * WordPress dependencies
- */
-import { useSelect } from '@wordpress/data';
-import { __EXPERIMENTAL_PATHS_WITH_MERGE as PATHS_WITH_MERGE } from '@wordpress/blocks';
-/**
- * Internal dependencies
- */
-import { store as editSiteStore } from '../../store';
+import { get, find, isString } from 'lodash';
/* Supporting data */
export const ROOT_BLOCK_NAME = 'root';
@@ -72,52 +63,28 @@ export const PRESET_METADATA = [
},
];
-const STYLE_PROPERTIES_TO_CSS_VAR_INFIX = {
- linkColor: 'color',
- backgroundColor: 'color',
- background: 'gradient',
+const STYLE_PATH_TO_CSS_VAR_INFIX = {
+ 'color.background': 'color',
+ 'color.text': 'color',
+ 'elements.link.color.text': 'color',
+ 'color.gradient': 'gradient',
+ 'typography.fontSize': 'font-size',
+ 'typography.fontFamily': 'font-family',
};
-function getPresetMetadataFromStyleProperty( styleProperty ) {
- if ( ! getPresetMetadataFromStyleProperty.MAP ) {
- getPresetMetadataFromStyleProperty.MAP = {};
- PRESET_METADATA.forEach( ( { cssVarInfix }, index ) => {
- getPresetMetadataFromStyleProperty.MAP[ camelCase( cssVarInfix ) ] =
- PRESET_METADATA[ index ];
- } );
- forEach( STYLE_PROPERTIES_TO_CSS_VAR_INFIX, ( value, key ) => {
- getPresetMetadataFromStyleProperty.MAP[ key ] =
- getPresetMetadataFromStyleProperty.MAP[ value ];
- } );
- }
- return getPresetMetadataFromStyleProperty.MAP[ styleProperty ];
-}
-
-export function useSetting( path, blockName = '' ) {
- const settings = useSelect( ( select ) => {
- return select( editSiteStore ).getSettings();
- } );
- const topLevelPath = `__experimentalFeatures.${ path }`;
- const blockPath = `__experimentalFeatures.blocks.${ blockName }.${ path }`;
- const result = get( settings, blockPath ) ?? get( settings, topLevelPath );
- if ( result && PATHS_WITH_MERGE[ path ] ) {
- return result.user ?? result.theme ?? result.core;
- }
- return result;
-}
-
function findInPresetsBy(
- styles,
- context,
+ features,
+ blockName,
presetPath,
presetProperty,
presetValueValue
) {
// Block presets take priority above root level presets.
const orderedPresetsByOrigin = [
- get( styles, [ 'settings', 'blocks', context, ...presetPath ] ),
- get( styles, [ 'settings', ...presetPath ] ),
+ get( features, [ 'blocks', blockName, ...presetPath ] ),
+ get( features, presetPath ),
];
+
for ( const presetByOrigin of orderedPresetsByOrigin ) {
if ( presetByOrigin ) {
// Preset origins ordered by priority.
@@ -136,8 +103,8 @@ function findInPresetsBy(
}
// if there is a highest priority preset with the same slug but different value the preset we found was overwritten and should be ignored.
const highestPresetObjectWithSameSlug = findInPresetsBy(
- styles,
- context,
+ features,
+ blockName,
presetPath,
'slug',
presetObject.slug
@@ -157,50 +124,57 @@ function findInPresetsBy(
}
}
-export function getPresetVariable( styles, context, propertyName, value ) {
- if ( ! value ) {
- return value;
+export function getPresetVariableFromValue(
+ features,
+ blockName,
+ variableStylePath,
+ presetPropertyValue
+) {
+ if ( ! presetPropertyValue ) {
+ return presetPropertyValue;
}
- const metadata = getPresetMetadataFromStyleProperty( propertyName );
+ const cssVarInfix = STYLE_PATH_TO_CSS_VAR_INFIX[ variableStylePath ];
+
+ const metadata = find( PRESET_METADATA, [ 'cssVarInfix', cssVarInfix ] );
+
if ( ! metadata ) {
// The property doesn't have preset data
// so the value should be returned as it is.
- return value;
+ return presetPropertyValue;
}
- const { valueKey, path, cssVarInfix } = metadata;
+ const { valueKey, path } = metadata;
const presetObject = findInPresetsBy(
- styles,
- context,
+ features,
+ blockName,
path,
valueKey,
- value
+ presetPropertyValue
);
if ( ! presetObject ) {
// Value wasn't found in the presets,
// so it must be a custom value.
- return value;
+ return presetPropertyValue;
}
return `var:preset|${ cssVarInfix }|${ presetObject.slug }`;
}
function getValueFromPresetVariable(
- styles,
+ features,
blockName,
variable,
[ presetType, slug ]
) {
- presetType = camelCase( presetType );
- const metadata = getPresetMetadataFromStyleProperty( presetType );
+ const metadata = find( PRESET_METADATA, [ 'cssVarInfix', presetType ] );
if ( ! metadata ) {
return variable;
}
const presetObject = findInPresetsBy(
- styles,
+ features,
blockName,
metadata.path,
'slug',
@@ -210,54 +184,63 @@ function getValueFromPresetVariable(
if ( presetObject ) {
const { valueKey } = metadata;
const result = presetObject[ valueKey ];
- return getValueFromVariable( styles, blockName, result );
+ return getValueFromVariable( features, blockName, result );
}
return variable;
}
-function getValueFromCustomVariable( styles, blockName, variable, path ) {
+function getValueFromCustomVariable( features, blockName, variable, path ) {
const result =
- get( styles, [ 'settings', 'blocks', blockName, 'custom', ...path ] ) ??
- get( styles, [ 'settings', 'custom', ...path ] );
+ get( features, [ 'blocks', blockName, 'custom', ...path ] ) ??
+ get( features, [ 'custom', ...path ] );
if ( ! result ) {
return variable;
}
// A variable may reference another variable so we need recursion until we find the value.
- return getValueFromVariable( styles, blockName, result );
+ return getValueFromVariable( features, blockName, result );
}
-export function getValueFromVariable( styles, blockName, variable ) {
+export function getValueFromVariable( features, blockName, variable ) {
if ( ! variable || ! isString( variable ) ) {
return variable;
}
+ const USER_VALUE_PREFIX = 'var:';
+ const THEME_VALUE_PREFIX = 'var(--wp--';
+ const THEME_VALUE_SUFFIX = ')';
let parsedVar;
- const INTERNAL_REFERENCE_PREFIX = 'var:';
- const CSS_REFERENCE_PREFIX = 'var(--wp--';
- const CSS_REFERENCE_SUFFIX = ')';
- if ( variable.startsWith( INTERNAL_REFERENCE_PREFIX ) ) {
- parsedVar = variable
- .slice( INTERNAL_REFERENCE_PREFIX.length )
- .split( '|' );
+
+ if ( variable.startsWith( USER_VALUE_PREFIX ) ) {
+ parsedVar = variable.slice( USER_VALUE_PREFIX.length ).split( '|' );
} else if (
- variable.startsWith( CSS_REFERENCE_PREFIX ) &&
- variable.endsWith( CSS_REFERENCE_SUFFIX )
+ variable.startsWith( THEME_VALUE_PREFIX ) &&
+ variable.endsWith( THEME_VALUE_SUFFIX )
) {
parsedVar = variable
- .slice( CSS_REFERENCE_PREFIX.length, -CSS_REFERENCE_SUFFIX.length )
+ .slice( THEME_VALUE_PREFIX.length, -THEME_VALUE_SUFFIX.length )
.split( '--' );
} else {
- // Value is raw.
+ // We don't know how to parse the value: either is raw of uses complex CSS such as `calc(1px * var(--wp--variable) )`
return variable;
}
const [ type, ...path ] = parsedVar;
if ( type === 'preset' ) {
- return getValueFromPresetVariable( styles, blockName, variable, path );
+ return getValueFromPresetVariable(
+ features,
+ blockName,
+ variable,
+ path
+ );
}
if ( type === 'custom' ) {
- return getValueFromCustomVariable( styles, blockName, variable, path );
+ return getValueFromCustomVariable(
+ features,
+ blockName,
+ variable,
+ path
+ );
}
return variable;
}
diff --git a/packages/edit-site/src/components/sidebar/global-styles-sidebar.js b/packages/edit-site/src/components/sidebar/global-styles-sidebar.js
index 78f106825117c2..628932a314fa28 100644
--- a/packages/edit-site/src/components/sidebar/global-styles-sidebar.js
+++ b/packages/edit-site/src/components/sidebar/global-styles-sidebar.js
@@ -8,9 +8,8 @@ import { styles } from '@wordpress/icons';
/**
* Internal dependencies
*/
-import { useGlobalStylesReset } from '../editor/global-styles-provider';
import DefaultSidebar from './default-sidebar';
-import GlobalStyles from '../global-styles';
+import { GlobalStylesUI, useGlobalStylesReset } from '../global-styles';
export default function GlobalStylesSidebar() {
const [ canRestart, onReset ] = useGlobalStylesReset();
@@ -40,7 +39,7 @@ export default function GlobalStylesSidebar() {
>
}
>
-
+
);
}