UNPKG

@wordpress/block-editor

Version:
357 lines (309 loc) 12 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.STYLE_PATH_TO_PRESET_BLOCK_ATTRIBUTE = exports.STYLE_PATH_TO_CSS_VAR_INFIX = exports.ROOT_BLOCK_SUPPORTS = exports.ROOT_BLOCK_SELECTOR = exports.ROOT_BLOCK_NAME = exports.PRESET_METADATA = void 0; exports.areGlobalStyleConfigsEqual = areGlobalStyleConfigsEqual; exports.getPresetVariableFromValue = getPresetVariableFromValue; exports.getValueFromVariable = getValueFromVariable; exports.scopeSelector = scopeSelector; var _lodash = require("lodash"); var _es = _interopRequireDefault(require("fast-deep-equal/es6")); var _typographyUtils = require("./typography-utils"); /** * External dependencies */ /** * Internal dependencies */ /* Supporting data. */ const ROOT_BLOCK_NAME = 'root'; exports.ROOT_BLOCK_NAME = ROOT_BLOCK_NAME; const ROOT_BLOCK_SELECTOR = 'body'; exports.ROOT_BLOCK_SELECTOR = ROOT_BLOCK_SELECTOR; const ROOT_BLOCK_SUPPORTS = ['background', 'backgroundColor', 'color', 'linkColor', 'captionColor', 'buttonColor', 'headingColor', 'fontFamily', 'fontSize', 'fontStyle', 'fontWeight', 'lineHeight', 'textDecoration', 'textTransform', 'padding']; exports.ROOT_BLOCK_SUPPORTS = ROOT_BLOCK_SUPPORTS; 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'], valueKey: 'colors', cssVarInfix: 'duotone', valueFunc: ({ slug }) => `url( '#wp-duotone-${slug}' )`, classes: [] }, { path: ['shadow', 'presets'], valueKey: 'shadow', cssVarInfix: 'shadow', classes: [] }, { path: ['typography', 'fontSizes'], valueFunc: (preset, settings) => (0, _typographyUtils.getTypographyFontSizeValue)(preset, (0, _typographyUtils.getFluidTypographyOptionsFromSettings)(settings)), 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: [] }]; exports.PRESET_METADATA = PRESET_METADATA; const STYLE_PATH_TO_CSS_VAR_INFIX = { 'color.background': 'color', 'color.text': 'color', 'filter.duotone': 'duotone', 'elements.link.color.text': 'color', 'elements.link.:hover.color.text': 'color', 'elements.link.typography.fontFamily': 'font-family', 'elements.link.typography.fontSize': 'font-size', 'elements.button.color.text': 'color', 'elements.button.color.background': 'color', 'elements.caption.color.text': 'color', 'elements.button.typography.fontFamily': 'font-family', 'elements.button.typography.fontSize': 'font-size', 'elements.heading.color': 'color', 'elements.heading.color.background': 'color', 'elements.heading.typography.fontFamily': 'font-family', 'elements.heading.gradient': 'gradient', 'elements.heading.color.gradient': 'gradient', 'elements.h1.color': 'color', 'elements.h1.color.background': 'color', 'elements.h1.typography.fontFamily': 'font-family', 'elements.h1.color.gradient': 'gradient', 'elements.h2.color': 'color', 'elements.h2.color.background': 'color', 'elements.h2.typography.fontFamily': 'font-family', 'elements.h2.color.gradient': 'gradient', 'elements.h3.color': 'color', 'elements.h3.color.background': 'color', 'elements.h3.typography.fontFamily': 'font-family', 'elements.h3.color.gradient': 'gradient', 'elements.h4.color': 'color', 'elements.h4.color.background': 'color', 'elements.h4.typography.fontFamily': 'font-family', 'elements.h4.color.gradient': 'gradient', 'elements.h5.color': 'color', 'elements.h5.color.background': 'color', 'elements.h5.typography.fontFamily': 'font-family', 'elements.h5.color.gradient': 'gradient', 'elements.h6.color': 'color', 'elements.h6.color.background': 'color', 'elements.h6.typography.fontFamily': 'font-family', 'elements.h6.color.gradient': 'gradient', 'color.gradient': 'gradient', shadow: 'shadow', 'typography.fontSize': 'font-size', 'typography.fontFamily': 'font-family' }; // A static list of block attributes that store global style preset slugs. exports.STYLE_PATH_TO_CSS_VAR_INFIX = STYLE_PATH_TO_CSS_VAR_INFIX; const STYLE_PATH_TO_PRESET_BLOCK_ATTRIBUTE = { 'color.background': 'backgroundColor', 'color.text': 'textColor', 'color.gradient': 'gradient', 'typography.fontSize': 'fontSize', 'typography.fontFamily': 'fontFamily' }; exports.STYLE_PATH_TO_PRESET_BLOCK_ATTRIBUTE = STYLE_PATH_TO_PRESET_BLOCK_ATTRIBUTE; function findInPresetsBy(features, blockName, presetPath, presetProperty, presetValueValue) { // Block presets take priority above root level presets. const orderedPresetsByOrigin = [(0, _lodash.get)(features, ['blocks', blockName, ...presetPath]), (0, _lodash.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 getPresetVariableFromValue(features, blockName, variableStylePath, presetPropertyValue) { if (!presetPropertyValue) { return presetPropertyValue; } const cssVarInfix = STYLE_PATH_TO_CSS_VAR_INFIX[variableStylePath]; const metadata = PRESET_METADATA.find(data => data.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}`; } 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) { var _get; const result = (_get = (0, _lodash.get)(features.settings, ['blocks', blockName, 'custom', ...path])) !== null && _get !== void 0 ? _get : (0, _lodash.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. */ function getValueFromVariable(features, blockName, variable) { if (!variable || typeof variable !== 'string') { if (variable?.ref && typeof variable?.ref === 'string') { const refPath = variable.ref.split('.'); variable = (0, _lodash.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. * * @example * ```js * const scope = '.a, .b .c'; * const selector = '> .x, .y'; * const merged = scopeSelector( scope, selector ); * // merged is '.a > .x, .a .y, .b .c > .x, .b .c .y' * ``` * * @param {string} scope Selector to scope to. * @param {string} selector Original selector. * * @return {string} Scoped selector. */ function scopeSelector(scope, selector) { const scopes = scope.split(','); const selectors = selector.split(','); const selectorsScoped = []; scopes.forEach(outer => { selectors.forEach(inner => { selectorsScoped.push(`${outer.trim()} ${inner.trim()}`); }); }); return selectorsScoped.join(', '); } /** * Compares global style variations according to their styles and settings properties. * * @example * ```js * const globalStyles = { styles: { typography: { fontSize: '10px' } }, settings: {} }; * const variation = { styles: { typography: { fontSize: '10000px' } }, settings: {} }; * const isEqual = areGlobalStyleConfigsEqual( globalStyles, variation ); * // false * ``` * * @param {Object} original A global styles object. * @param {Object} variation A global styles object. * * @return {boolean} Whether `original` and `variation` match. */ function areGlobalStyleConfigsEqual(original, variation) { if (typeof original !== 'object' || typeof variation !== 'object') { return original === variation; } return (0, _es.default)(original?.styles, variation?.styles) && (0, _es.default)(original?.settings, variation?.settings); } //# sourceMappingURL=utils.js.map