diff --git a/lib/compat/wordpress-6.2/block-editor-settings.php b/lib/compat/wordpress-6.2/block-editor-settings.php index 7323d34eb667ce..7f40429acf3076 100644 --- a/lib/compat/wordpress-6.2/block-editor-settings.php +++ b/lib/compat/wordpress-6.2/block-editor-settings.php @@ -15,11 +15,12 @@ function gutenberg_get_block_editor_settings_6_2( $settings ) { if ( wp_theme_has_theme_json() ) { // Add the custom CSS as separate style sheet so any invalid CSS entered by users does not break other global styles. - $settings['styles'][] = array( + $settings['styles'][] = array( 'css' => gutenberg_get_global_stylesheet( array( 'custom-css' ) ), '__unstableType' => 'user', 'isGlobalStyles' => true, ); + $settings['__experimentalStyles'] = wp_get_global_styles(); } return $settings; diff --git a/lib/experimental/class-wp-rest-block-editor-settings-controller.php b/lib/experimental/class-wp-rest-block-editor-settings-controller.php index 7031a14e89ccac..43735477b9ca14 100644 --- a/lib/experimental/class-wp-rest-block-editor-settings-controller.php +++ b/lib/experimental/class-wp-rest-block-editor-settings-controller.php @@ -153,7 +153,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' ), ), '__experimentalEnableQuoteBlockV2' => array( diff --git a/packages/block-editor/README.md b/packages/block-editor/README.md index 5a861eaa1acad7..52b14587e8391c 100644 --- a/packages/block-editor/README.md +++ b/packages/block-editor/README.md @@ -348,6 +348,10 @@ _Returns_ Undocumented declaration. +### findInPresetsBy + +Undocumented declaration. + ### FontSizePicker _Related_ @@ -527,6 +531,35 @@ _Returns_ - `Object`: Typography block support derived CSS classes & styles. +### getTypographyFontSizeValue + +Returns a font-size value based on a given font-size preset. +Takes into account fluid typography parameters and attempts to return a css formula depending on available, valid values. + +_Parameters_ + +- _preset_ `Preset`: +- _typographySettings_ `Object`: +- _typographySettings.fluid_ `boolean|TypographySettings`: Whether fluid typography is enabled, and, optionally, fluid font size options. + +_Returns_ + +- `string|*`: A font-size value or the value of preset.size. + +### getValueFromVariable + +Attempts to fetch the value of a theme.json CSS variable. + +_Parameters_ + +- _features_ `Object`: GlobalStylesContext config, e.g., user, base or merged. Represents the theme.json tree. +- _blockName_ `string`: The name of a block as represented in the styles property. E.g., 'root' for root-level, and 'core/${blockName}' for blocks. +- _variable_ `string|*`: An incoming style value. A CSS var value is expected, but it could be any value. + +_Returns_ + +- `string|*|{ref}`: The value of the CSS var, if found. If not found, the passed variable argument. + ### InnerBlocks _Related_ @@ -616,6 +649,10 @@ _Related_ - +### PRESET_METADATA + +Undocumented declaration. + ### RichText _Related_ diff --git a/packages/block-editor/src/components/index.js b/packages/block-editor/src/components/index.js index 03ce8f3880ad32..a674b8c931455d 100644 --- a/packages/block-editor/src/components/index.js +++ b/packages/block-editor/src/components/index.js @@ -164,3 +164,4 @@ export { default as __experimentalInspectorPopoverHeader } from './inspector-pop 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 670d6fac37e8df..4c8c1ac83504c1 100644 --- a/packages/block-editor/src/components/line-height-control/index.js +++ b/packages/block-editor/src/components/line-height-control/index.js @@ -21,6 +21,7 @@ const LineHeightControl = ( { /** Start opting into the new margin-free styles that will become the default in a future version. */ __nextHasNoMarginBottom = false, __unstableInputWidth = '60px', + placeholder = BASE_DEFAULT_VALUE, ...otherProps } ) => { const isDefined = isLineHeightDefined( lineHeight ); @@ -95,7 +96,7 @@ const LineHeightControl = ( { __unstableStateReducer={ stateReducer } onChange={ onChange } label={ __( 'Line height' ) } - placeholder={ BASE_DEFAULT_VALUE } + placeholder={ placeholder } step={ STEP } value={ value } min={ 0 } 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 c8397d850a1e55..99a6b0ccf90d4d 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 }, setAttributes, } = props; + const defaultLineHeight = useStyle( [ 'typography', 'lineHeight' ] ); const onChange = ( newLineHeightValue ) => { const newStyle = { @@ -43,6 +45,7 @@ export function LineHeightEdit( props ) { value={ style?.typography?.lineHeight } onChange={ onChange } size="__unstable-large" + placeholder={ defaultLineHeight } /> ); } diff --git a/packages/block-editor/src/utils/index.js b/packages/block-editor/src/utils/index.js index af45111759699c..a934618667bfff 100644 --- a/packages/block-editor/src/utils/index.js +++ b/packages/block-editor/src/utils/index.js @@ -1,3 +1,9 @@ export { default as transformStyles } from './transform-styles'; export * from './block-variation-transforms'; export { default as getPxFromCssUnit } from './parse-css-unit-to-px'; +export { + PRESET_METADATA, + findInPresetsBy, + getValueFromVariable, +} from './style-variable-resolution'; +export { getTypographyFontSizeValue } from './typography'; 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..17a9062cccbe1b --- /dev/null +++ b/packages/block-editor/src/utils/style-variable-resolution.js @@ -0,0 +1,222 @@ +/** + * External dependencies + */ +import { get } from 'lodash'; + +/** + * Internal dependencies + */ +import { getTypographyFontSizeValue } from './typography'; + +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: [ 'color', 'duotone' ], + cssVarInfix: 'duotone', + valueFunc: ( { slug } ) => `url( '#wp-duotone-${ slug }' )`, + classes: [], + }, + { + path: [ 'typography', 'fontSizes' ], + valueFunc: ( preset, { typography: typographySettings } ) => + getTypographyFontSizeValue( preset, typographySettings ), + valueKey: 'size', + cssVarInfix: 'font-size', + classes: [ { classSuffix: 'font-size', propertyName: 'font-size' } ], + }, + { + path: [ 'typography', 'fontFamilies' ], + valueKey: 'fontFamily', + cssVarInfix: 'font-family', + classes: [ + { classSuffix: 'font-family', propertyName: 'font-family' }, + ], + }, + { + path: [ 'spacing', 'spacingSizes' ], + valueKey: 'size', + cssVarInfix: 'spacing', + valueFunc: ( { size } ) => size, + classes: [], + }, +]; + +export 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 = [ 'custom', 'theme', 'default' ]; + for ( const origin of origins ) { + const presets = presetByOrigin[ origin ]; + if ( presets ) { + const presetObject = presets.find( + ( 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 = PRESET_METADATA.find( + ( data ) => data.cssVarInfix === presetType + ); + if ( ! metadata ) { + return variable; + } + + const presetObject = findInPresetsBy( + features.settings, + 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.settings, [ 'blocks', blockName, 'custom', ...path ] ) ?? + get( features.settings, [ '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 ); +} + +/** + * Attempts to fetch the value of a theme.json CSS variable. + * + * @param {Object} features GlobalStylesContext config, e.g., user, base or merged. Represents the theme.json tree. + * @param {string} blockName The name of a block as represented in the styles property. E.g., 'root' for root-level, and 'core/${blockName}' for blocks. + * @param {string|*} variable An incoming style value. A CSS var value is expected, but it could be any value. + * @return {string|*|{ref}} The value of the CSS var, if found. If not found, the passed variable argument. + */ +export function getValueFromVariable( features, blockName, variable ) { + if ( ! variable || typeof variable !== 'string' ) { + if ( variable?.ref && typeof variable?.ref === 'string' ) { + const refPath = variable.ref.split( '.' ); + variable = get( features, refPath ); + // Presence of another ref indicates a reference to another dynamic value. + // Pointing to another dynamic value is not supported. + if ( ! variable || !! variable?.ref ) { + return variable; + } + } else { + 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; +} diff --git a/packages/block-editor/src/utils/test/style-variable-resolution.js b/packages/block-editor/src/utils/test/style-variable-resolution.js new file mode 100644 index 00000000000000..64796e6ac8c374 --- /dev/null +++ b/packages/block-editor/src/utils/test/style-variable-resolution.js @@ -0,0 +1,149 @@ +/** + * Internal dependencies + */ +import { getValueFromVariable } from '../style-variable-resolution'; + +describe( 'editor utils', () => { + const themeJson = { + version: 1, + settings: { + color: { + palette: { + theme: [ + { + slug: 'primary', + color: '#007cba', + name: 'Primary', + }, + { + slug: 'secondary', + color: '#006ba1', + name: 'Secondary', + }, + ], + custom: [ + { + slug: 'primary', + color: '#007cba', + name: 'Primary', + }, + { + slug: 'secondary', + color: '#a65555', + name: 'Secondary', + }, + ], + }, + custom: true, + customDuotone: true, + customGradient: true, + link: true, + }, + custom: { + color: { + primary: 'var(--wp--preset--color--primary)', + secondary: 'var(--wp--preset--color--secondary)', + }, + }, + }, + isGlobalStylesUserThemeJSON: true, + }; + + describe( 'getValueFromVariable', () => { + describe( 'when provided an invalid variable', () => { + it( 'returns the originally provided value', () => { + const actual = getValueFromVariable( + themeJson, + 'root', + undefined + ); + + expect( actual ).toBe( undefined ); + } ); + } ); + + describe( 'when provided a preset variable', () => { + it( 'retrieves the correct preset value', () => { + const actual = getValueFromVariable( + themeJson, + 'root', + 'var:preset|color|primary' + ); + + expect( actual ).toBe( '#007cba' ); + } ); + } ); + + describe( 'when provided a custom variable', () => { + it( 'retrieves the correct custom value', () => { + const actual = getValueFromVariable( + themeJson, + 'root', + 'var(--wp--custom--color--secondary)' + ); + + expect( actual ).toBe( '#a65555' ); + } ); + } ); + + describe( 'when provided a dynamic reference', () => { + it( 'retrieves the referenced value', () => { + const stylesWithRefs = { + ...themeJson, + styles: { + color: { + background: { + ref: 'styles.color.text', + }, + text: 'purple-rain', + }, + }, + }; + const actual = getValueFromVariable( stylesWithRefs, 'root', { + ref: 'styles.color.text', + } ); + + expect( actual ).toBe( stylesWithRefs.styles.color.text ); + } ); + + it( 'returns the originally provided value where value is dynamic reference and reference does not exist', () => { + const stylesWithRefs = { + ...themeJson, + styles: { + color: { + text: { + ref: 'styles.background.text', + }, + }, + }, + }; + const actual = getValueFromVariable( stylesWithRefs, 'root', { + ref: 'styles.color.text', + } ); + + expect( actual ).toBe( stylesWithRefs.styles.color.text ); + } ); + + it( 'returns the originally provided value where value is dynamic reference', () => { + const stylesWithRefs = { + ...themeJson, + styles: { + color: { + background: { + ref: 'styles.color.text', + }, + text: { + ref: 'styles.background.text', + }, + }, + }, + }; + const actual = getValueFromVariable( stylesWithRefs, 'root', { + ref: 'styles.color.text', + } ); + + expect( actual ).toBe( stylesWithRefs.styles.color.text ); + } ); + } ); + } ); +} ); diff --git a/packages/edit-site/src/components/global-styles/test/typography-utils.js b/packages/block-editor/src/utils/test/typography.js similarity index 99% rename from packages/edit-site/src/components/global-styles/test/typography-utils.js rename to packages/block-editor/src/utils/test/typography.js index 9d0213cb53e554..f78b5f9d4811c1 100644 --- a/packages/edit-site/src/components/global-styles/test/typography-utils.js +++ b/packages/block-editor/src/utils/test/typography.js @@ -1,7 +1,7 @@ /** * Internal dependencies */ -import { getTypographyFontSizeValue } from '../typography-utils'; +import { getTypographyFontSizeValue } from '../typography'; describe( 'typography utils', () => { describe( 'getTypographyFontSizeValue', () => { diff --git a/packages/edit-site/src/components/global-styles/typography-utils.js b/packages/block-editor/src/utils/typography.js similarity index 96% rename from packages/edit-site/src/components/global-styles/typography-utils.js rename to packages/block-editor/src/utils/typography.js index 5720fdeb6ce91e..53df510646e342 100644 --- a/packages/edit-site/src/components/global-styles/typography-utils.js +++ b/packages/block-editor/src/utils/typography.js @@ -5,9 +5,9 @@ */ /** - * WordPress dependencies + * Internal dependencies */ -import { getComputedFluidTypographyValue } from '@wordpress/block-editor'; +import { getComputedFluidTypographyValue } from '../components/font-sizes'; /** * @typedef {Object} FluidPreset diff --git a/packages/edit-site/src/components/global-styles/hooks.js b/packages/edit-site/src/components/global-styles/hooks.js index c767f1a488cbfd..a78daea6d6954a 100644 --- a/packages/edit-site/src/components/global-styles/hooks.js +++ b/packages/edit-site/src/components/global-styles/hooks.js @@ -16,11 +16,12 @@ import { __EXPERIMENTAL_PATHS_WITH_MERGE as PATHS_WITH_MERGE, __EXPERIMENTAL_STYLE_PROPERTY as STYLE_PROPERTY, } from '@wordpress/blocks'; +import { getValueFromVariable } from '@wordpress/block-editor'; /** * Internal dependencies */ -import { getValueFromVariable, getPresetVariableFromValue } from './utils'; +import { getPresetVariableFromValue } from './utils'; import { GlobalStylesContext } from './context'; // Enable colord's a11y plugin. diff --git a/packages/edit-site/src/components/global-styles/test/utils.js b/packages/edit-site/src/components/global-styles/test/utils.js index 7d0e3464557f61..c4742df4bf5ad9 100644 --- a/packages/edit-site/src/components/global-styles/test/utils.js +++ b/packages/edit-site/src/components/global-styles/test/utils.js @@ -1,7 +1,7 @@ /** * Internal dependencies */ -import { getPresetVariableFromValue, getValueFromVariable } from '../utils'; +import { getPresetVariableFromValue } from '../utils'; describe( 'editor utils', () => { const themeJson = { @@ -105,102 +105,4 @@ describe( 'editor utils', () => { } ); } ); } ); - - describe( 'getValueFromVariable', () => { - describe( 'when provided an invalid variable', () => { - it( 'returns the originally provided value', () => { - const actual = getValueFromVariable( - themeJson, - 'root', - undefined - ); - - expect( actual ).toBe( undefined ); - } ); - } ); - - describe( 'when provided a preset variable', () => { - it( 'retrieves the correct preset value', () => { - const actual = getValueFromVariable( - themeJson, - 'root', - 'var:preset|color|primary' - ); - - expect( actual ).toBe( '#007cba' ); - } ); - } ); - - describe( 'when provided a custom variable', () => { - it( 'retrieves the correct custom value', () => { - const actual = getValueFromVariable( - themeJson, - 'root', - 'var(--wp--custom--color--secondary)' - ); - - expect( actual ).toBe( '#a65555' ); - } ); - } ); - - describe( 'when provided a dynamic reference', () => { - it( 'retrieves the referenced value', () => { - const stylesWithRefs = { - ...themeJson, - styles: { - color: { - background: { - ref: 'styles.color.text', - }, - text: 'purple-rain', - }, - }, - }; - const actual = getValueFromVariable( stylesWithRefs, 'root', { - ref: 'styles.color.text', - } ); - - expect( actual ).toBe( stylesWithRefs.styles.color.text ); - } ); - - it( 'returns the originally provided value where value is dynamic reference and reference does not exist', () => { - const stylesWithRefs = { - ...themeJson, - styles: { - color: { - text: { - ref: 'styles.background.text', - }, - }, - }, - }; - const actual = getValueFromVariable( stylesWithRefs, 'root', { - ref: 'styles.color.text', - } ); - - expect( actual ).toBe( stylesWithRefs.styles.color.text ); - } ); - - it( 'returns the originally provided value where value is dynamic reference', () => { - const stylesWithRefs = { - ...themeJson, - styles: { - color: { - background: { - ref: 'styles.color.text', - }, - text: { - ref: 'styles.background.text', - }, - }, - }, - }; - const actual = getValueFromVariable( stylesWithRefs, 'root', { - ref: 'styles.color.text', - } ); - - expect( actual ).toBe( stylesWithRefs.styles.color.text ); - } ); - } ); - } ); } ); 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 index d83693757b035c..cdddec63d54a4f 100644 --- 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 @@ -18,13 +18,14 @@ import { __unstablePresetDuotoneFilter as PresetDuotoneFilter, __experimentalGetGapCSSValue as getGapCSSValue, store as blockEditorStore, + PRESET_METADATA, + getTypographyFontSizeValue, } from '@wordpress/block-editor'; /** * Internal dependencies */ -import { PRESET_METADATA, ROOT_BLOCK_SELECTOR, scopeSelector } from './utils'; -import { getTypographyFontSizeValue } from './typography-utils'; +import { ROOT_BLOCK_SELECTOR, scopeSelector } from './utils'; import { GlobalStylesContext } from './context'; import { useSetting } from './hooks'; diff --git a/packages/edit-site/src/components/global-styles/utils.js b/packages/edit-site/src/components/global-styles/utils.js index 14f3b868294172..0ebd53139073ca 100644 --- a/packages/edit-site/src/components/global-styles/utils.js +++ b/packages/edit-site/src/components/global-styles/utils.js @@ -1,12 +1,7 @@ /** - * External dependencies + * WordPress dependencies */ -import { get } from 'lodash'; - -/** - * Internal dependencies - */ -import { getTypographyFontSizeValue } from './typography-utils'; +import { PRESET_METADATA, findInPresetsBy } from '@wordpress/block-editor'; /* Supporting data. */ export const ROOT_BLOCK_NAME = 'root'; @@ -27,65 +22,6 @@ export const ROOT_BLOCK_SUPPORTS = [ 'padding', ]; -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: [ 'color', 'duotone' ], - cssVarInfix: 'duotone', - valueFunc: ( { slug } ) => `url( '#wp-duotone-${ slug }' )`, - classes: [], - }, - { - path: [ 'typography', 'fontSizes' ], - valueFunc: ( preset, { typography: typographySettings } ) => - getTypographyFontSizeValue( preset, typographySettings ), - valueKey: 'size', - cssVarInfix: 'font-size', - classes: [ { classSuffix: 'font-size', propertyName: 'font-size' } ], - }, - { - path: [ 'typography', 'fontFamilies' ], - valueKey: 'fontFamily', - cssVarInfix: 'font-family', - classes: [ - { classSuffix: 'font-family', propertyName: 'font-family' }, - ], - }, - { - path: [ 'spacing', 'spacingSizes' ], - valueKey: 'size', - cssVarInfix: 'spacing', - valueFunc: ( { size } ) => size, - classes: [], - }, -]; - export const STYLE_PATH_TO_CSS_VAR_INFIX = { 'color.background': 'color', 'color.text': 'color', @@ -109,57 +45,6 @@ export const STYLE_PATH_TO_PRESET_BLOCK_ATTRIBUTE = { 'typography.fontFamily': 'fontFamily', }; -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 = [ 'custom', 'theme', 'default' ]; - for ( const origin of origins ) { - const presets = presetByOrigin[ origin ]; - if ( presets ) { - const presetObject = presets.find( - ( 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; - } - } - } - } - } -} - export function getPresetVariableFromValue( features, blockName, @@ -200,109 +85,6 @@ export function getPresetVariableFromValue( return `var:preset|${ cssVarInfix }|${ presetObject.slug }`; } -function getValueFromPresetVariable( - features, - blockName, - variable, - [ presetType, slug ] -) { - const metadata = PRESET_METADATA.find( - ( data ) => data.cssVarInfix === presetType - ); - if ( ! metadata ) { - return variable; - } - - const presetObject = findInPresetsBy( - features.settings, - 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.settings, [ 'blocks', blockName, 'custom', ...path ] ) ?? - get( features.settings, [ '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 ); -} - -/** - * Attempts to fetch the value of a theme.json CSS variable. - * - * @param {Object} features GlobalStylesContext config, e.g., user, base or merged. Represents the theme.json tree. - * @param {string} blockName The name of a block as represented in the styles property. E.g., 'root' for root-level, and 'core/${blockName}' for blocks. - * @param {string|*} variable An incoming style value. A CSS var value is expected, but it could be any value. - * @return {string|*|{ref}} The value of the CSS var, if found. If not found, the passed variable argument. - */ -export function getValueFromVariable( features, blockName, variable ) { - if ( ! variable || typeof variable !== 'string' ) { - if ( variable?.ref && typeof variable?.ref === 'string' ) { - const refPath = variable.ref.split( '.' ); - variable = get( features, refPath ); - // Presence of another ref indicates a reference to another dynamic value. - // Pointing to another dynamic value is not supported. - if ( ! variable || !! variable?.ref ) { - return variable; - } - } else { - 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; -} - /** * Function that scopes a selector with another one. This works a bit like * SCSS nesting except the `&` operator isn't supported. 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 70ab1ac333c44a..efb0b3570b959a 100644 --- a/packages/editor/src/components/provider/use-block-editor-settings.js +++ b/packages/editor/src/components/provider/use-block-editor-settings.js @@ -141,6 +141,7 @@ function useBlockEditorSettings( settings, hasTemplate ) { '__experimentalPreferredStyleVariations', '__experimentalSetIsInserterOpened', '__unstableGalleryWithImageBlocks', + '__experimentalStyles', 'alignWide', 'allowedBlockTypes', 'bodyPlaceholder',