UNPKG

@wordpress/block-editor

Version:
404 lines (389 loc) 13.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.addSaveProps = addSaveProps; exports.default = void 0; exports.getInlineStyles = getInlineStyles; exports.omitStyle = omitStyle; var _element = require("@wordpress/element"); var _hooks = require("@wordpress/hooks"); var _blocks = require("@wordpress/blocks"); var _compose = require("@wordpress/compose"); var _styleEngine = require("@wordpress/style-engine"); var _background = require("./background"); var _border = require("./border"); var _color = require("./color"); var _typography = require("./typography"); var _dimensions = require("./dimensions"); var _utils = require("./utils"); var _utils2 = require("../components/global-styles/utils"); var _blockEditingMode = require("../components/block-editing-mode"); var _jsxRuntime = require("react/jsx-runtime"); /** * WordPress dependencies */ /** * Internal dependencies */ const styleSupportKeys = [..._typography.TYPOGRAPHY_SUPPORT_KEYS, _border.BORDER_SUPPORT_KEY, _color.COLOR_SUPPORT_KEY, _dimensions.DIMENSIONS_SUPPORT_KEY, _background.BACKGROUND_SUPPORT_KEY, _dimensions.SPACING_SUPPORT_KEY, _border.SHADOW_SUPPORT_KEY]; const hasStyleSupport = nameOrType => styleSupportKeys.some(key => (0, _blocks.hasBlockSupport)(nameOrType, key)); /** * Returns the inline styles to add depending on the style object * * @param {Object} styles Styles configuration. * * @return {Object} Flattened CSS variables declaration. */ function getInlineStyles(styles = {}) { const output = {}; // The goal is to move everything to server side generated engine styles // This is temporary as we absorb more and more styles into the engine. (0, _styleEngine.getCSSRules)(styles).forEach(rule => { output[rule.key] = rule.value; }); return output; } /** * Filters registered block settings, extending attributes to include `style` attribute. * * @param {Object} settings Original block settings. * * @return {Object} Filtered block settings. */ function addAttribute(settings) { if (!hasStyleSupport(settings)) { return settings; } // Allow blocks to specify their own attribute definition with default values if needed. if (!settings.attributes.style) { Object.assign(settings.attributes, { style: { type: 'object' } }); } return settings; } /** * A dictionary of paths to flag skipping block support serialization as the key, * with values providing the style paths to be omitted from serialization. * * @constant * @type {Record<string, string[]>} */ const skipSerializationPathsEdit = { [`${_border.BORDER_SUPPORT_KEY}.__experimentalSkipSerialization`]: ['border'], [`${_color.COLOR_SUPPORT_KEY}.__experimentalSkipSerialization`]: [_color.COLOR_SUPPORT_KEY], [`${_typography.TYPOGRAPHY_SUPPORT_KEY}.__experimentalSkipSerialization`]: [_typography.TYPOGRAPHY_SUPPORT_KEY], [`${_dimensions.DIMENSIONS_SUPPORT_KEY}.__experimentalSkipSerialization`]: [_dimensions.DIMENSIONS_SUPPORT_KEY], [`${_dimensions.SPACING_SUPPORT_KEY}.__experimentalSkipSerialization`]: [_dimensions.SPACING_SUPPORT_KEY], [`${_border.SHADOW_SUPPORT_KEY}.__experimentalSkipSerialization`]: [_border.SHADOW_SUPPORT_KEY] }; /** * A dictionary of paths to flag skipping block support serialization as the key, * with values providing the style paths to be omitted from serialization. * * Extends the Edit skip paths to enable skipping additional paths in just * the Save component. This allows a block support to be serialized within the * editor, while using an alternate approach, such as server-side rendering, when * the support is saved. * * @constant * @type {Record<string, string[]>} */ const skipSerializationPathsSave = { ...skipSerializationPathsEdit, [`${_dimensions.DIMENSIONS_SUPPORT_KEY}.aspectRatio`]: [`${_dimensions.DIMENSIONS_SUPPORT_KEY}.aspectRatio`], // Skip serialization of aspect ratio in save mode. [`${_background.BACKGROUND_SUPPORT_KEY}`]: [_background.BACKGROUND_SUPPORT_KEY] // Skip serialization of background support in save mode. }; const skipSerializationPathsSaveChecks = { [`${_dimensions.DIMENSIONS_SUPPORT_KEY}.aspectRatio`]: true, [`${_background.BACKGROUND_SUPPORT_KEY}`]: true }; /** * A dictionary used to normalize feature names between support flags, style * object properties and __experimentSkipSerialization configuration arrays. * * This allows not having to provide a migration for a support flag and possible * backwards compatibility bridges, while still achieving consistency between * the support flag and the skip serialization array. * * @constant * @type {Record<string, string>} */ const renamedFeatures = { gradients: 'gradient' }; /** * A utility function used to remove one or more paths from a style object. * Works in a way similar to Lodash's `omit()`. See unit tests and examples below. * * It supports a single string path: * * ``` * omitStyle( { color: 'red' }, 'color' ); // {} * ``` * * or an array of paths: * * ``` * omitStyle( { color: 'red', background: '#fff' }, [ 'color', 'background' ] ); // {} * ``` * * It also allows you to specify paths at multiple levels in a string. * * ``` * omitStyle( { typography: { textDecoration: 'underline' } }, 'typography.textDecoration' ); // {} * ``` * * You can remove multiple paths at the same time: * * ``` * omitStyle( * { * typography: { * textDecoration: 'underline', * textTransform: 'uppercase', * } * }, * [ * 'typography.textDecoration', * 'typography.textTransform', * ] * ); * // {} * ``` * * You can also specify nested paths as arrays: * * ``` * omitStyle( * { * typography: { * textDecoration: 'underline', * textTransform: 'uppercase', * } * }, * [ * [ 'typography', 'textDecoration' ], * [ 'typography', 'textTransform' ], * ] * ); * // {} * ``` * * With regards to nesting of styles, infinite depth is supported: * * ``` * omitStyle( * { * border: { * radius: { * topLeft: '10px', * topRight: '0.5rem', * } * } * }, * [ * [ 'border', 'radius', 'topRight' ], * ] * ); * // { border: { radius: { topLeft: '10px' } } } * ``` * * The third argument, `preserveReference`, defines how to treat the input style object. * It is mostly necessary to properly handle mutation when recursively handling the style object. * Defaulting to `false`, this will always create a new object, avoiding to mutate `style`. * However, when recursing, we change that value to `true` in order to work with a single copy * of the original style object. * * @see https://lodash.com/docs/4.17.15#omit * * @param {Object} style Styles object. * @param {Array|string} paths Paths to remove. * @param {boolean} preserveReference True to mutate the `style` object, false otherwise. * @return {Object} Styles object with the specified paths removed. */ function omitStyle(style, paths, preserveReference = false) { if (!style) { return style; } let newStyle = style; if (!preserveReference) { newStyle = JSON.parse(JSON.stringify(style)); } if (!Array.isArray(paths)) { paths = [paths]; } paths.forEach(path => { if (!Array.isArray(path)) { path = path.split('.'); } if (path.length > 1) { const [firstSubpath, ...restPath] = path; omitStyle(newStyle[firstSubpath], [restPath], true); } else if (path.length === 1) { delete newStyle[path[0]]; } }); return newStyle; } /** * Override props assigned to save component to inject the CSS variables definition. * * @param {Object} props Additional props applied to save element. * @param {Object|string} blockNameOrType Block type. * @param {Object} attributes Block attributes. * @param {?Record<string, string[]>} skipPaths An object of keys and paths to skip serialization. * * @return {Object} Filtered props applied to save element. */ function addSaveProps(props, blockNameOrType, attributes, skipPaths = skipSerializationPathsSave) { if (!hasStyleSupport(blockNameOrType)) { return props; } let { style } = attributes; Object.entries(skipPaths).forEach(([indicator, path]) => { const skipSerialization = skipSerializationPathsSaveChecks[indicator] || (0, _blocks.getBlockSupport)(blockNameOrType, indicator); if (skipSerialization === true) { style = omitStyle(style, path); } if (Array.isArray(skipSerialization)) { skipSerialization.forEach(featureName => { const feature = renamedFeatures[featureName] || featureName; style = omitStyle(style, [[...path, feature]]); }); } }); props.style = { ...getInlineStyles(style), ...props.style }; return props; } function BlockStyleControls({ clientId, name, setAttributes, __unstableParentLayout }) { const settings = (0, _utils.useBlockSettings)(name, __unstableParentLayout); const blockEditingMode = (0, _blockEditingMode.useBlockEditingMode)(); const passedProps = { clientId, name, setAttributes, settings: { ...settings, typography: { ...settings.typography, // The text alignment UI for individual blocks is rendered in // the block toolbar, so disable it here. textAlign: false } } }; if (blockEditingMode !== 'default') { return null; } return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, { children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_color.ColorEdit, { ...passedProps }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_background.BackgroundImagePanel, { ...passedProps }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_typography.TypographyPanel, { ...passedProps }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_border.BorderPanel, { ...passedProps }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_dimensions.DimensionsPanel, { ...passedProps })] }); } var _default = exports.default = { edit: BlockStyleControls, hasSupport: hasStyleSupport, addSaveProps, attributeKeys: ['style'], useBlockProps }; // Defines which element types are supported, including their hover styles or // any other elements that have been included under a single element type // e.g. heading and h1-h6. const elementTypes = [{ elementType: 'button' }, { elementType: 'link', pseudo: [':hover'] }, { elementType: 'heading', elements: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'] }]; // Used for generating the instance ID const STYLE_BLOCK_PROPS_REFERENCE = {}; function useBlockProps({ name, style }) { const blockElementsContainerIdentifier = (0, _compose.useInstanceId)(STYLE_BLOCK_PROPS_REFERENCE, 'wp-elements'); const baseElementSelector = `.${blockElementsContainerIdentifier}`; const blockElementStyles = style?.elements; const styles = (0, _element.useMemo)(() => { if (!blockElementStyles) { return; } const elementCSSRules = []; elementTypes.forEach(({ elementType, pseudo, elements }) => { const skipSerialization = (0, _utils.shouldSkipSerialization)(name, _color.COLOR_SUPPORT_KEY, elementType); if (skipSerialization) { return; } const elementStyles = blockElementStyles?.[elementType]; // Process primary element type styles. if (elementStyles) { const selector = (0, _utils2.scopeSelector)(baseElementSelector, _blocks.__EXPERIMENTAL_ELEMENTS[elementType]); elementCSSRules.push((0, _styleEngine.compileCSS)(elementStyles, { selector })); // Process any interactive states for the element type. if (pseudo) { pseudo.forEach(pseudoSelector => { if (elementStyles[pseudoSelector]) { elementCSSRules.push((0, _styleEngine.compileCSS)(elementStyles[pseudoSelector], { selector: (0, _utils2.scopeSelector)(baseElementSelector, `${_blocks.__EXPERIMENTAL_ELEMENTS[elementType]}${pseudoSelector}`) })); } }); } } // Process related elements e.g. h1-h6 for headings if (elements) { elements.forEach(element => { if (blockElementStyles[element]) { elementCSSRules.push((0, _styleEngine.compileCSS)(blockElementStyles[element], { selector: (0, _utils2.scopeSelector)(baseElementSelector, _blocks.__EXPERIMENTAL_ELEMENTS[element]) })); } }); } }); return elementCSSRules.length > 0 ? elementCSSRules.join('') : undefined; }, [baseElementSelector, blockElementStyles, name]); (0, _utils.useStyleOverride)({ css: styles }); return addSaveProps({ className: blockElementsContainerIdentifier }, name, { style }, skipSerializationPathsEdit); } (0, _hooks.addFilter)('blocks.registerBlockType', 'core/style/addAttribute', addAttribute); //# sourceMappingURL=style.js.map