UNPKG

@wordpress/block-editor

Version:
326 lines (314 loc) 11.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.__unstableBlockStyleVariationOverridesWithConfig = __unstableBlockStyleVariationOverridesWithConfig; exports.default = void 0; exports.getVariationStylesWithRefValues = getVariationStylesWithRefValues; var _blocks = require("@wordpress/blocks"); var _data = require("@wordpress/data"); var _element = require("@wordpress/element"); var _globalStyles = require("../components/global-styles"); var _utils = require("./utils"); var _object = require("../utils/object"); var _store = require("../store"); var _privateKeys = require("../store/private-keys"); var _lockUnlock = require("../lock-unlock"); var _jsxRuntime = require("react/jsx-runtime"); /** * WordPress dependencies */ /** * Internal dependencies */ const VARIATION_PREFIX = 'is-style-'; function getVariationMatches(className) { if (!className) { return []; } return className.split(/\s+/).reduce((matches, name) => { if (name.startsWith(VARIATION_PREFIX)) { const match = name.slice(VARIATION_PREFIX.length); if (match !== 'default') { matches.push(match); } } return matches; }, []); } /** * Get the first block style variation that has been registered from the class string. * * @param {string} className CSS class string for a block. * @param {Array} registeredStyles Currently registered block styles. * * @return {string|null} The name of the first registered variation. */ function getVariationNameFromClass(className, registeredStyles = []) { // The global flag affects how capturing groups work in JS. So the regex // below will only return full CSS classes not just the variation name. const matches = getVariationMatches(className); if (!matches) { return null; } for (const variation of matches) { if (registeredStyles.some(style => style.name === variation)) { return variation; } } return null; } // A helper component to apply a style override using the useStyleOverride hook. function OverrideStyles({ override }) { (0, _utils.usePrivateStyleOverride)(override); } /** * This component is used to generate new block style variation overrides * based on an incoming theme config. If a matching style is found in the config, * a new override is created and returned. The overrides can be used in conjunction with * useStyleOverride to apply the new styles to the editor. Its use is * subject to change. * * @param {Object} props Props. * @param {Object} props.config A global styles object, containing settings and styles. * @return {JSX.Element|undefined} An array of new block variation overrides. */ function __unstableBlockStyleVariationOverridesWithConfig({ config }) { const { getBlockStyles, overrides } = (0, _data.useSelect)(select => ({ getBlockStyles: select(_blocks.store).getBlockStyles, overrides: (0, _lockUnlock.unlock)(select(_store.store)).getStyleOverrides() }), []); const { getBlockName } = (0, _data.useSelect)(_store.store); const overridesWithConfig = (0, _element.useMemo)(() => { if (!overrides?.length) { return; } const newOverrides = []; const overriddenClientIds = []; for (const [, override] of overrides) { if (override?.variation && override?.clientId && /* * Because this component overwrites existing style overrides, * filter out any overrides that are already present in the store. */ !overriddenClientIds.includes(override.clientId)) { const blockName = getBlockName(override.clientId); const configStyles = config?.styles?.blocks?.[blockName]?.variations?.[override.variation]; if (configStyles) { const variationConfig = { settings: config?.settings, // The variation style data is all that is needed to generate // the styles for the current application to a block. The variation // name is updated to match the instance specific class name. styles: { blocks: { [blockName]: { variations: { [`${override.variation}-${override.clientId}`]: configStyles } } } } }; const blockSelectors = (0, _globalStyles.getBlockSelectors)((0, _blocks.getBlockTypes)(), getBlockStyles, override.clientId); const hasBlockGapSupport = false; const hasFallbackGapSupport = true; const disableLayoutStyles = true; const disableRootPadding = true; const variationStyles = (0, _globalStyles.toStyles)(variationConfig, blockSelectors, hasBlockGapSupport, hasFallbackGapSupport, disableLayoutStyles, disableRootPadding, { blockGap: false, blockStyles: true, layoutStyles: false, marginReset: false, presets: false, rootPadding: false, variationStyles: true }); newOverrides.push({ id: `${override.variation}-${override.clientId}`, css: variationStyles, __unstableType: 'variation', variation: override.variation, // The clientId will be stored with the override and used to ensure // the order of overrides matches the order of blocks so that the // correct CSS cascade is maintained. clientId: override.clientId }); overriddenClientIds.push(override.clientId); } } } return newOverrides; }, [config, overrides, getBlockStyles, getBlockName]); if (!overridesWithConfig || !overridesWithConfig.length) { return; } return /*#__PURE__*/(0, _jsxRuntime.jsx)(_jsxRuntime.Fragment, { children: overridesWithConfig.map(override => /*#__PURE__*/(0, _jsxRuntime.jsx)(OverrideStyles, { override: override }, override.id)) }); } /** * Retrieves any variation styles data and resolves any referenced values. * * @param {Object} globalStyles A complete global styles object, containing settings and styles. * @param {string} name The name of the desired block type. * @param {variation} variation The of the block style variation to retrieve data for. * * @return {Object|undefined} The global styles data for the specified variation. */ function getVariationStylesWithRefValues(globalStyles, name, variation) { if (!globalStyles?.styles?.blocks?.[name]?.variations?.[variation]) { return; } // Helper to recursively look for `ref` values to resolve. const replaceRefs = variationStyles => { Object.keys(variationStyles).forEach(key => { const value = variationStyles[key]; // Only process objects. if (typeof value === 'object' && value !== null) { // Process `ref` value if present. if (value.ref !== undefined) { if (typeof value.ref !== 'string' || value.ref.trim() === '') { // Remove invalid ref. delete variationStyles[key]; } else { // Resolve `ref` value. const refValue = (0, _object.getValueFromObjectPath)(globalStyles, value.ref); if (refValue) { variationStyles[key] = refValue; } else { delete variationStyles[key]; } } } else { // Recursively resolve `ref` values in nested objects. replaceRefs(value); // After recursion, if value is empty due to explicitly // `undefined` ref value, remove it. if (Object.keys(value).length === 0) { delete variationStyles[key]; } } } }); }; // Deep clone variation node to avoid mutating it within global styles and losing refs. const styles = JSON.parse(JSON.stringify(globalStyles.styles.blocks[name].variations[variation])); replaceRefs(styles); return styles; } function useBlockStyleVariation(name, variation, clientId) { // Prefer global styles data in GlobalStylesContext, which are available // if in the site editor. Otherwise fall back to whatever is in the // editor settings and available in the post editor. const { merged: mergedConfig } = (0, _element.useContext)(_globalStyles.GlobalStylesContext); const { globalSettings, globalStyles } = (0, _data.useSelect)(select => { const settings = select(_store.store).getSettings(); return { globalSettings: settings.__experimentalFeatures, globalStyles: settings[_privateKeys.globalStylesDataKey] }; }, []); return (0, _element.useMemo)(() => { var _mergedConfig$setting, _mergedConfig$styles, _mergedConfig$setting2; const variationStyles = getVariationStylesWithRefValues({ settings: (_mergedConfig$setting = mergedConfig?.settings) !== null && _mergedConfig$setting !== void 0 ? _mergedConfig$setting : globalSettings, styles: (_mergedConfig$styles = mergedConfig?.styles) !== null && _mergedConfig$styles !== void 0 ? _mergedConfig$styles : globalStyles }, name, variation); return { settings: (_mergedConfig$setting2 = mergedConfig?.settings) !== null && _mergedConfig$setting2 !== void 0 ? _mergedConfig$setting2 : globalSettings, // The variation style data is all that is needed to generate // the styles for the current application to a block. The variation // name is updated to match the instance specific class name. styles: { blocks: { [name]: { variations: { [`${variation}-${clientId}`]: variationStyles } } } } }; }, [mergedConfig, globalSettings, globalStyles, variation, clientId, name]); } // Rather than leveraging `useInstanceId` here, the `clientId` is used. // This is so that the variation style override's ID is predictable // when the order of applied style variations changes. function useBlockProps({ name, className, clientId }) { const { getBlockStyles } = (0, _data.useSelect)(_blocks.store); const registeredStyles = getBlockStyles(name); const variation = getVariationNameFromClass(className, registeredStyles); const variationClass = `${VARIATION_PREFIX}${variation}-${clientId}`; const { settings, styles } = useBlockStyleVariation(name, variation, clientId); const variationStyles = (0, _element.useMemo)(() => { if (!variation) { return; } const variationConfig = { settings, styles }; const blockSelectors = (0, _globalStyles.getBlockSelectors)((0, _blocks.getBlockTypes)(), getBlockStyles, clientId); const hasBlockGapSupport = false; const hasFallbackGapSupport = true; const disableLayoutStyles = true; const disableRootPadding = true; return (0, _globalStyles.toStyles)(variationConfig, blockSelectors, hasBlockGapSupport, hasFallbackGapSupport, disableLayoutStyles, disableRootPadding, { blockGap: false, blockStyles: true, layoutStyles: false, marginReset: false, presets: false, rootPadding: false, variationStyles: true }); }, [variation, settings, styles, getBlockStyles, clientId]); (0, _utils.usePrivateStyleOverride)({ id: `variation-${clientId}`, css: variationStyles, __unstableType: 'variation', variation, // The clientId will be stored with the override and used to ensure // the order of overrides matches the order of blocks so that the // correct CSS cascade is maintained. clientId }); return variation ? { className: variationClass } : {}; } var _default = exports.default = { hasSupport: () => true, attributeKeys: ['className'], isMatch: ({ className }) => getVariationMatches(className).length > 0, useBlockProps }; //# sourceMappingURL=block-style-variation.js.map