diff --git a/lib/class-wp-rest-block-editor-settings-controller.php b/lib/class-wp-rest-block-editor-settings-controller.php index 1d66d1f903b0e5..46bca84400ba54 100644 --- a/lib/class-wp-rest-block-editor-settings-controller.php +++ b/lib/class-wp-rest-block-editor-settings-controller.php @@ -149,7 +149,7 @@ public function get_item_schema() { '__experimentalStyles' => array( 'description' => __( 'Styles consolidated from core, theme, and user origins.', 'gutenberg' ), 'type' => 'object', - 'context' => array( 'mobile' ), + 'context' => array( 'post-editor', 'widgets-editor', 'mobile' ), ), 'alignWide' => array( diff --git a/lib/global-styles.php b/lib/global-styles.php index a749a66c40cf0d..0cf5e4bb92415d 100644 --- a/lib/global-styles.php +++ b/lib/global-styles.php @@ -111,7 +111,7 @@ function_exists( 'gutenberg_is_edit_site_page' ) && } $consolidated = WP_Theme_JSON_Resolver_Gutenberg::get_merged_data( $settings, $origin ); - if ( 'mobile' === $context ) { + if ( 'site-editor' !== $context ) { $settings['__experimentalStyles'] = $consolidated->get_raw_data()['styles']; } diff --git a/packages/block-editor/src/components/index.js b/packages/block-editor/src/components/index.js index 99c1042b991730..b81a1e897a49a0 100644 --- a/packages/block-editor/src/components/index.js +++ b/packages/block-editor/src/components/index.js @@ -147,3 +147,4 @@ export { default as __experimentalUseNoRecursiveRenders } from './use-no-recursi export { default as BlockEditorProvider } from './provider'; export { default as useSetting } from './use-setting'; +export { default as __experimentalUseStyle } from './use-style'; diff --git a/packages/block-editor/src/components/line-height-control/index.js b/packages/block-editor/src/components/line-height-control/index.js index f4b5261b40acee..c3c0f74908c338 100644 --- a/packages/block-editor/src/components/line-height-control/index.js +++ b/packages/block-editor/src/components/line-height-control/index.js @@ -15,7 +15,11 @@ import { isLineHeightDefined, } from './utils'; -export default function LineHeightControl( { value: lineHeight, onChange } ) { +export default function LineHeightControl( { + value: lineHeight, + onChange, + placeholder = BASE_DEFAULT_VALUE, +} ) { const isDefined = isLineHeightDefined( lineHeight ); const handleOnKeyDown = ( event ) => { @@ -70,7 +74,7 @@ export default function LineHeightControl( { value: lineHeight, onChange } ) { onKeyDown={ handleOnKeyDown } onChange={ handleOnChange } label={ __( 'Line height' ) } - placeholder={ BASE_DEFAULT_VALUE } + placeholder={ placeholder } step={ STEP } type="number" value={ value } diff --git a/packages/block-editor/src/components/use-style/index.js b/packages/block-editor/src/components/use-style/index.js new file mode 100644 index 00000000000000..b7d30274502a70 --- /dev/null +++ b/packages/block-editor/src/components/use-style/index.js @@ -0,0 +1,51 @@ +/** + * External dependencies + */ +import { get } from 'lodash'; + +/** + * WordPress dependencies + */ +import { useSelect } from '@wordpress/data'; +import { useMemo } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import { useBlockEditContext } from '../block-edit'; +import { store as blockEditorStore } from '../../store'; +import { getValueFromVariable } from '../../utils/style-variable-resolution'; + +/** + * Hook that retrieves the global styles of a block. + * It works with nested objects using by finding the value at path. + * + * @param {string|Array} path The path to the setting. + * + * @return {any} Returns the style value defined for the path. + * + * @example + * ```js + * const backgroundColor = useStyle( 'color.background' ); + * ``` + */ +export default function useStyle( path ) { + const { name: blockName } = useBlockEditContext(); + + const settings = useSelect( ( select ) => { + return select( blockEditorStore ).getSettings(); + }, [] ); + const stylesForBlock = get( settings, [ + '__experimentalStyles', + 'blocks', + blockName, + ] ); + const value = get( stylesForBlock, path ); + return useMemo( () => { + return getValueFromVariable( + settings.__experimentalFeatures, + blockName, + value + ); + }, [ settings.__experimentalFeatures, blockName, value ] ); +} diff --git a/packages/block-editor/src/hooks/line-height.js b/packages/block-editor/src/hooks/line-height.js index 1d57366878b214..315106332b6ddd 100644 --- a/packages/block-editor/src/hooks/line-height.js +++ b/packages/block-editor/src/hooks/line-height.js @@ -9,6 +9,7 @@ import { hasBlockSupport } from '@wordpress/blocks'; import LineHeightControl from '../components/line-height-control'; import { cleanEmptyObject } from './utils'; import useSetting from '../components/use-setting'; +import useStyle from '../components/use-style'; export const LINE_HEIGHT_SUPPORT_KEY = 'typography.lineHeight'; @@ -24,6 +25,7 @@ export function LineHeightEdit( props ) { attributes: { style }, } = props; const isDisabled = useIsLineHeightDisabled( props ); + const defaultLineHeight = useStyle( [ 'typography', 'lineHeight' ] ); if ( isDisabled ) { return null; @@ -45,6 +47,7 @@ export function LineHeightEdit( props ) { ); } diff --git a/packages/block-editor/src/utils/index.js b/packages/block-editor/src/utils/index.js index f498a6bf4740bb..498376b2d90fcb 100644 --- a/packages/block-editor/src/utils/index.js +++ b/packages/block-editor/src/utils/index.js @@ -1,3 +1,7 @@ export { default as transformStyles } from './transform-styles'; export * from './theme'; export * from './block-variation-transforms'; +export { + getValueFromVariable as __experimentalGetValueFromVariable, + getPresetVariableFromValue as __experimentalGetPresetVariableFromValue, +} from './style-variable-resolution'; diff --git a/packages/block-editor/src/utils/style-variable-resolution.js b/packages/block-editor/src/utils/style-variable-resolution.js new file mode 100644 index 00000000000000..c369b4becf182f --- /dev/null +++ b/packages/block-editor/src/utils/style-variable-resolution.js @@ -0,0 +1,188 @@ +/** + * External dependencies + */ +import { get, find, isString, kebabCase } from 'lodash'; + +/** + * WordPress dependencies + */ +import { __EXPERIMENTAL_PRESET_METADATA as PRESET_METADATA } from '@wordpress/blocks'; + +const STYLE_PROPERTIES_TO_CSS_VAR_INFIX = { + linkColor: 'color', + backgroundColor: 'color', + background: 'gradient', +}; + +function findInPresetsBy( + features, + blockName, + presetPath, + presetProperty, + presetValueValue +) { + // Block presets take priority above root level presets. + const orderedPresetsByOrigin = [ + get( features, [ 'blocks', blockName, ...presetPath ] ), + get( features, presetPath ), + ]; + for ( const presetByOrigin of orderedPresetsByOrigin ) { + if ( presetByOrigin ) { + // Preset origins ordered by priority. + const origins = [ 'user', 'theme', 'core' ]; + for ( const origin of origins ) { + const presets = presetByOrigin[ origin ]; + if ( presets ) { + const presetObject = find( + presets, + ( preset ) => + preset[ presetProperty ] === presetValueValue + ); + if ( presetObject ) { + if ( presetProperty === 'slug' ) { + return presetObject; + } + // 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( + features, + blockName, + presetPath, + 'slug', + presetObject.slug + ); + if ( + highestPresetObjectWithSameSlug[ + presetProperty + ] === presetObject[ presetProperty ] + ) { + return presetObject; + } + return undefined; + } + } + } + } + } +} + +function getValueFromPresetVariable( + features, + blockName, + variable, + [ presetType, slug ] +) { + const metadata = find( PRESET_METADATA, [ 'cssVarInfix', presetType ] ); + if ( ! metadata ) { + return variable; + } + + const presetObject = findInPresetsBy( + features, + blockName, + metadata.path, + 'slug', + slug + ); + + if ( presetObject ) { + const { valueKey } = metadata; + const result = presetObject[ valueKey ]; + return getValueFromVariable( features, blockName, result ); + } + + return variable; +} + +function getValueFromCustomVariable( features, blockName, variable, path ) { + const result = + 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( features, blockName, result ); +} + +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; + + if ( variable.startsWith( USER_VALUE_PREFIX ) ) { + parsedVar = variable.slice( USER_VALUE_PREFIX.length ).split( '|' ); + } else if ( + variable.startsWith( THEME_VALUE_PREFIX ) && + variable.endsWith( THEME_VALUE_SUFFIX ) + ) { + parsedVar = variable + .slice( THEME_VALUE_PREFIX.length, -THEME_VALUE_SUFFIX.length ) + .split( '--' ); + } else { + // 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( + features, + blockName, + variable, + path + ); + } + if ( type === 'custom' ) { + return getValueFromCustomVariable( + features, + blockName, + variable, + path + ); + } + return variable; +} + +export function getPresetVariableFromValue( + features, + blockName, + presetPropertyName, + presetPropertyValue +) { + if ( ! presetPropertyValue ) { + return presetPropertyValue; + } + + const cssVarInfix = + STYLE_PROPERTIES_TO_CSS_VAR_INFIX[ presetPropertyName ] || + kebabCase( presetPropertyName ); + + 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 presetPropertyValue; + } + const { valueKey, path } = metadata; + + const presetObject = findInPresetsBy( + features, + blockName, + path, + valueKey, + presetPropertyValue + ); + + if ( ! presetObject ) { + // Value wasn't found in the presets, + // so it must be a custom value. + return presetPropertyValue; + } + + return `var:preset|${ cssVarInfix }|${ presetObject.slug }`; +} diff --git a/packages/block-editor/src/utils/test/get-preset-variable-from-value.js b/packages/block-editor/src/utils/test/get-preset-variable-from-value.js new file mode 100644 index 00000000000000..4f35d47aea6ca4 --- /dev/null +++ b/packages/block-editor/src/utils/test/get-preset-variable-from-value.js @@ -0,0 +1,402 @@ +/** + * Internal dependencies + */ +import { getPresetVariableFromValue } from '../style-variable-resolution'; + +describe( 'getPresetVariableFromValue', () => { + it( 'returns the block preset variable when the value matches both the global & block presets', () => { + const presetPropertyValue = '#ff0000'; + const features = { + color: { + palette: { + core: [ + { + name: 'Red vintage', + slug: 'red-vintage', + color: presetPropertyValue, + }, + ], + theme: [], + user: [], + }, + }, + blocks: { + 'core/paragraph': { + color: { + palette: { + core: [ + { + name: 'red', + slug: 'red', + color: presetPropertyValue, + }, + ], + theme: [], + user: [], + }, + }, + }, + }, + }; + const blockName = 'core/paragraph'; + const presetPropertyName = 'color'; + const result = getPresetVariableFromValue( + features, + blockName, + presetPropertyName, + presetPropertyValue + ); + expect( result ).toBe( 'var:preset|color|red' ); + } ); + + it( 'returns the global preset variable when the value matches only the global preset', () => { + const presetPropertyValue = '#990F02'; + const features = { + color: { + palette: { + core: [ + { + name: 'Red cherry', + slug: 'red-cherry', + color: presetPropertyValue, + }, + ], + theme: [], + user: [], + }, + }, + blocks: { + 'core/paragraph': { + color: { + palette: { + core: [ + { name: 'red', slug: 'red', color: '#ff0000' }, + ], + theme: [], + user: [], + }, + }, + }, + }, + }; + const blockName = 'core/paragraph'; + const presetPropertyName = 'color'; + const result = getPresetVariableFromValue( + features, + blockName, + presetPropertyName, + presetPropertyValue + ); + expect( result ).toBe( 'var:preset|color|red-cherry' ); + } ); + + it( 'returns the block preset variable when the value matches only the block preset', () => { + const presetPropertyValue = '#ff0000'; + const features = { + color: { + palette: { + core: [ + { + name: 'Red cherry', + slug: 'red-cherry', + color: '#990F02', + }, + ], + theme: [], + user: [], + }, + }, + blocks: { + 'core/paragraph': { + color: { + palette: { + core: [ + { + name: 'red', + slug: 'red', + color: presetPropertyValue, + }, + ], + theme: [], + user: [], + }, + }, + }, + }, + }; + const blockName = 'core/paragraph'; + const presetPropertyName = 'color'; + const result = getPresetVariableFromValue( + features, + blockName, + presetPropertyName, + presetPropertyValue + ); + expect( result ).toBe( 'var:preset|color|red' ); + } ); + + it( 'returns the user-provided preset variable when the value matches in core, theme, and user presets', () => { + const presetPropertyValue = '#990F02'; + const features = { + color: { + palette: { + core: [], + theme: [], + user: [], + }, + }, + blocks: { + 'core/paragraph': { + color: { + palette: { + core: [ + { + slug: 'red-cherry', + color: presetPropertyValue, + }, + ], + theme: [ + { + slug: 'red-vintage', + color: presetPropertyValue, + }, + ], + user: [ + { slug: 'red', color: presetPropertyValue }, + ], + }, + }, + }, + }, + }; + const blockName = 'core/paragraph'; + const presetPropertyName = 'color'; + const result = getPresetVariableFromValue( + features, + blockName, + presetPropertyName, + presetPropertyValue + ); + expect( result ).toBe( 'var:preset|color|red' ); + } ); + + it( 'returns the user-provided preset variable when the value matches only the user preset', () => { + const presetPropertyValue = '#990F02'; + const features = { + color: { + palette: { + core: [], + theme: [], + user: [], + }, + }, + blocks: { + 'core/paragraph': { + color: { + palette: { + core: [ + { + slug: 'red-cherry', + color: '#ff0000', + }, + ], + theme: [ + { + slug: 'red-vintage', + color: '#ff0000', + }, + ], + user: [ + { slug: 'red', color: presetPropertyValue }, + ], + }, + }, + }, + }, + }; + const blockName = 'core/paragraph'; + const presetPropertyName = 'color'; + const result = getPresetVariableFromValue( + features, + blockName, + presetPropertyName, + presetPropertyValue + ); + expect( result ).toBe( 'var:preset|color|red' ); + } ); + + it( 'returns the theme-provided preset variable when the value matches both core & theme presets', () => { + const presetPropertyValue = '#990F02'; + const features = { + color: { + palette: { + core: [], + theme: [], + user: [], + }, + }, + blocks: { + 'core/paragraph': { + color: { + palette: { + core: [ + { + slug: 'red-cherry', + color: presetPropertyValue, + }, + ], + theme: [ + { + slug: 'red-vintage', + color: presetPropertyValue, + }, + ], + user: [ { slug: 'red', color: '#ff0000' } ], + }, + }, + }, + }, + }; + const blockName = 'core/paragraph'; + const presetPropertyName = 'color'; + const result = getPresetVariableFromValue( + features, + blockName, + presetPropertyName, + presetPropertyValue + ); + expect( result ).toBe( 'var:preset|color|red-vintage' ); + } ); + + it( 'returns the theme-provided preset variable when the value matches only the theme preset', () => { + const presetPropertyValue = '#990F02'; + const features = { + color: { + palette: { + core: [], + theme: [], + user: [], + }, + }, + blocks: { + 'core/paragraph': { + color: { + palette: { + core: [ + { + slug: 'red-cherry', + color: '#ff0000', + }, + ], + theme: [ + { + slug: 'red-vintage', + color: presetPropertyValue, + }, + ], + user: [ { slug: 'red', color: '#ff0000' } ], + }, + }, + }, + }, + }; + const blockName = 'core/paragraph'; + const presetPropertyName = 'color'; + const result = getPresetVariableFromValue( + features, + blockName, + presetPropertyName, + presetPropertyValue + ); + expect( result ).toBe( 'var:preset|color|red-vintage' ); + } ); + + it( 'returns the core-provided preset variable when the value matches only the core preset', () => { + const presetPropertyValue = '#990F02'; + const features = { + color: { + palette: { + core: [], + theme: [], + user: [], + }, + }, + blocks: { + 'core/paragraph': { + color: { + palette: { + core: [ + { + slug: 'red-cherry', + color: presetPropertyValue, + }, + ], + theme: [ + { + slug: 'red-vintage', + color: '#ff0000', + }, + ], + user: [ { slug: 'red', color: '#ff0000' } ], + }, + }, + }, + }, + }; + const blockName = 'core/paragraph'; + const presetPropertyName = 'color'; + const result = getPresetVariableFromValue( + features, + blockName, + presetPropertyName, + presetPropertyValue + ); + expect( result ).toBe( 'var:preset|color|red-cherry' ); + } ); + + it( 'returns the same value when the preset has been overriden by a higher priority origin', () => { + const presetPropertyValue = '#990F02'; + const features = { + color: { + palette: { + core: [], + theme: [], + user: [], + }, + }, + blocks: { + 'core/paragraph': { + color: { + palette: { + core: [ + { + slug: 'red', + color: presetPropertyValue, + }, + ], + theme: [ + { + slug: 'red', + color: presetPropertyValue, + }, + ], + user: [ { slug: 'red', color: '#ff0000' } ], + }, + }, + }, + }, + }; + const blockName = 'core/paragraph'; + const presetPropertyName = 'color'; + const result = getPresetVariableFromValue( + features, + blockName, + presetPropertyName, + presetPropertyValue + ); + // The theme-provided value matches the given presetPropertyValue. + // However, the user has overriden that preset (same slug), + // hence we should ignore the theme-provided preset. + expect( result ).toBe( presetPropertyValue ); + } ); +} ); diff --git a/packages/blocks/src/api/constants.js b/packages/blocks/src/api/constants.js index d7ce7cafd25261..d54dbfee6a5570 100644 --- a/packages/blocks/src/api/constants.js +++ b/packages/blocks/src/api/constants.js @@ -124,3 +124,45 @@ export const __EXPERIMENTAL_ELEMENTS = { h5: 'h5', h6: 'h6', }; + +export const __EXPERIMENTAL_PRESET_METADATA = [ + { + path: [ 'color', 'palette' ], + valueKey: 'color', + cssVarInfix: 'color', + classes: [ + { classSuffix: 'color', propertyName: 'color' }, + { + classSuffix: 'background-color', + propertyName: 'background-color', + }, + { + classSuffix: 'border-color', + propertyName: 'border-color', + }, + ], + }, + { + path: [ 'color', 'gradients' ], + valueKey: 'gradient', + cssVarInfix: 'gradient', + classes: [ + { + classSuffix: 'gradient-background', + propertyName: 'background', + }, + ], + }, + { + path: [ 'typography', 'fontSizes' ], + valueKey: 'size', + cssVarInfix: 'font-size', + classes: [ { classSuffix: 'font-size', propertyName: 'font-size' } ], + }, + { + path: [ 'typography', 'fontFamilies' ], + valueKey: 'fontFamily', + cssVarInfix: 'font-family', + classes: [], + }, +]; diff --git a/packages/blocks/src/api/index.js b/packages/blocks/src/api/index.js index 394fddaa5de6e3..fc22e6cebfa2ef 100644 --- a/packages/blocks/src/api/index.js +++ b/packages/blocks/src/api/index.js @@ -159,4 +159,5 @@ export { default as node } from './node'; export { __EXPERIMENTAL_STYLE_PROPERTY, __EXPERIMENTAL_ELEMENTS, + __EXPERIMENTAL_PRESET_METADATA, } from './constants'; diff --git a/packages/edit-site/src/components/editor/global-styles-provider.js b/packages/edit-site/src/components/editor/global-styles-provider.js index d85aecdf251bcc..181c4d9a2be452 100644 --- a/packages/edit-site/src/components/editor/global-styles-provider.js +++ b/packages/edit-site/src/components/editor/global-styles-provider.js @@ -16,10 +16,15 @@ import { import { __EXPERIMENTAL_STYLE_PROPERTY as STYLE_PROPERTY, __EXPERIMENTAL_ELEMENTS as ELEMENTS, + __EXPERIMENTAL_PRESET_METADATA as PRESET_METADATA, store as blocksStore, } from '@wordpress/blocks'; import { useEntityProp } from '@wordpress/core-data'; import { useSelect, useDispatch } from '@wordpress/data'; +import { + __experimentalGetValueFromVariable as getValueFromVariable, + __experimentalGetPresetVariableFromValue as getPresetVariableFromValue, +} from '@wordpress/block-editor'; /** * Internal dependencies @@ -28,9 +33,6 @@ 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'; @@ -256,7 +258,11 @@ export default function GlobalStylesProvider( { children, baseStyles } ) { if ( origin === 'theme' ) { const value = get( themeStyles?.styles, path ); - return getValueFromVariable( themeStyles, context, value ); + return getValueFromVariable( + themeStyles.settings, + context, + value + ); } if ( origin === 'user' ) { @@ -265,11 +271,19 @@ export default function GlobalStylesProvider( { children, baseStyles } ) { // 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 ); + return getValueFromVariable( + mergedStyles.settings, + context, + value + ); } const value = get( mergedStyles?.styles, path ); - return getValueFromVariable( mergedStyles, context, value ); + return getValueFromVariable( + mergedStyles.settings, + context, + value + ); }, setStyle: ( context, propertyName, newValue ) => { const newContent = { ...userStyles }; @@ -288,8 +302,8 @@ export default function GlobalStylesProvider( { children, baseStyles } ) { set( newStyles, propertyPath, - getPresetVariable( - mergedStyles, + getPresetVariableFromValue( + mergedStyles.settings, context, propertyName, newValue @@ -323,6 +337,7 @@ export default function GlobalStylesProvider( { children, baseStyles } ) { }, ], __experimentalFeatures: mergedStyles.settings, + __experimentalStyles: mergedStyles.styles, } ); }, [ blocks, mergedStyles ] ); 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..caa6b6fd744ee8 100644 --- a/packages/edit-site/src/components/editor/global-styles-renderer.js +++ b/packages/edit-site/src/components/editor/global-styles-renderer.js @@ -20,12 +20,13 @@ import { import { __EXPERIMENTAL_STYLE_PROPERTY as STYLE_PROPERTY, __EXPERIMENTAL_ELEMENTS as ELEMENTS, + __EXPERIMENTAL_PRESET_METADATA as PRESET_METADATA, } from '@wordpress/blocks'; /** * Internal dependencies */ -import { PRESET_METADATA, ROOT_BLOCK_SELECTOR } from './utils'; +import { ROOT_BLOCK_SELECTOR } from './utils'; function compileStyleValue( uncompiledValue ) { const VARIABLE_REFERENCE_PREFIX = 'var:'; diff --git a/packages/edit-site/src/components/editor/utils.js b/packages/edit-site/src/components/editor/utils.js index ba883f330ea264..49148154997824 100644 --- a/packages/edit-site/src/components/editor/utils.js +++ b/packages/edit-site/src/components/editor/utils.js @@ -1,11 +1,12 @@ /** * External dependencies */ -import { get, find, forEach, camelCase, isString } from 'lodash'; +import { get } from 'lodash'; /** * WordPress dependencies */ import { useSelect } from '@wordpress/data'; + /** * Internal dependencies */ @@ -28,69 +29,6 @@ export const ROOT_BLOCK_SUPPORTS = [ 'textTransform', ]; -export const PRESET_METADATA = [ - { - path: [ 'color', 'palette' ], - valueKey: 'color', - cssVarInfix: 'color', - classes: [ - { classSuffix: 'color', propertyName: 'color' }, - { - classSuffix: 'background-color', - propertyName: 'background-color', - }, - { - classSuffix: 'border-color', - propertyName: 'border-color', - }, - ], - }, - { - path: [ 'color', 'gradients' ], - valueKey: 'gradient', - cssVarInfix: 'gradient', - classes: [ - { - classSuffix: 'gradient-background', - propertyName: 'background', - }, - ], - }, - { - path: [ 'typography', 'fontSizes' ], - valueKey: 'size', - cssVarInfix: 'font-size', - classes: [ { classSuffix: 'font-size', propertyName: 'font-size' } ], - }, - { - path: [ 'typography', 'fontFamilies' ], - valueKey: 'fontFamily', - cssVarInfix: 'font-family', - classes: [], - }, -]; - -const STYLE_PROPERTIES_TO_CSS_VAR_INFIX = { - linkColor: 'color', - backgroundColor: 'color', - background: 'gradient', -}; - -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 ]; -} - const PATHS_WITH_MERGE = { 'color.gradients': true, 'color.palette': true, @@ -110,159 +48,3 @@ export function useSetting( path, blockName = '' ) { } return result; } - -function findInPresetsBy( - styles, - context, - presetPath, - presetProperty, - presetValueValue -) { - // Block presets take priority above root level presets. - const orderedPresetsByOrigin = [ - get( styles, [ 'settings', 'blocks', context, ...presetPath ] ), - get( styles, [ 'settings', ...presetPath ] ), - ]; - for ( const presetByOrigin of orderedPresetsByOrigin ) { - if ( presetByOrigin ) { - // Preset origins ordered by priority. - const origins = [ 'user', 'theme', 'core' ]; - for ( const origin of origins ) { - const presets = presetByOrigin[ origin ]; - if ( presets ) { - const presetObject = find( - presets, - ( preset ) => - preset[ presetProperty ] === presetValueValue - ); - if ( presetObject ) { - if ( presetProperty === 'slug' ) { - return presetObject; - } - // 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, - presetPath, - 'slug', - presetObject.slug - ); - if ( - highestPresetObjectWithSameSlug[ - presetProperty - ] === presetObject[ presetProperty ] - ) { - return presetObject; - } - return undefined; - } - } - } - } - } -} - -export function getPresetVariable( styles, context, propertyName, value ) { - if ( ! value ) { - return value; - } - - const metadata = getPresetMetadataFromStyleProperty( propertyName ); - if ( ! metadata ) { - // The property doesn't have preset data - // so the value should be returned as it is. - return value; - } - const { valueKey, path, cssVarInfix } = metadata; - - const presetObject = findInPresetsBy( - styles, - context, - path, - valueKey, - value - ); - - if ( ! presetObject ) { - // Value wasn't found in the presets, - // so it must be a custom value. - return value; - } - - return `var:preset|${ cssVarInfix }|${ presetObject.slug }`; -} - -function getValueFromPresetVariable( - styles, - blockName, - variable, - [ presetType, slug ] -) { - presetType = camelCase( presetType ); - const metadata = getPresetMetadataFromStyleProperty( presetType ); - if ( ! metadata ) { - return variable; - } - - const presetObject = findInPresetsBy( - styles, - blockName, - metadata.path, - 'slug', - slug - ); - - if ( presetObject ) { - const { valueKey } = metadata; - const result = presetObject[ valueKey ]; - return getValueFromVariable( styles, blockName, result ); - } - - return variable; -} - -function getValueFromCustomVariable( styles, blockName, variable, path ) { - const result = - get( styles, [ 'settings', 'blocks', blockName, 'custom', ...path ] ) ?? - get( styles, [ 'settings', '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 ); -} - -export function getValueFromVariable( styles, blockName, variable ) { - if ( ! variable || ! isString( variable ) ) { - return variable; - } - - 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( '|' ); - } else if ( - variable.startsWith( CSS_REFERENCE_PREFIX ) && - variable.endsWith( CSS_REFERENCE_SUFFIX ) - ) { - parsedVar = variable - .slice( CSS_REFERENCE_PREFIX.length, -CSS_REFERENCE_SUFFIX.length ) - .split( '--' ); - } else { - // Value is raw. - return variable; - } - - const [ type, ...path ] = parsedVar; - if ( type === 'preset' ) { - return getValueFromPresetVariable( styles, blockName, variable, path ); - } - if ( type === 'custom' ) { - return getValueFromCustomVariable( styles, blockName, variable, path ); - } - return variable; -} diff --git a/packages/editor/src/components/provider/use-block-editor-settings.js b/packages/editor/src/components/provider/use-block-editor-settings.js index 41d00d360fc15e..c120c375a89156 100644 --- a/packages/editor/src/components/provider/use-block-editor-settings.js +++ b/packages/editor/src/components/provider/use-block-editor-settings.js @@ -72,6 +72,7 @@ function useBlockEditorSettings( settings, hasTemplate ) { '__experimentalBlockDirectory', '__experimentalBlockPatternCategories', '__experimentalBlockPatterns', + '__experimentalStyles', '__experimentalFeatures', '__experimentalGlobalStylesBaseStyles', '__experimentalGlobalStylesUserEntityId',