UNPKG

@wordpress/block-editor

Version:
596 lines (595 loc) 15.8 kB
// packages/block-editor/src/hooks/utils.js import { getBlockSupport } from "@wordpress/blocks"; import { memo, useMemo, useEffect, useId, useState } from "@wordpress/element"; import { useDispatch, useRegistry } from "@wordpress/data"; import { createHigherOrderComponent } from "@wordpress/compose"; import { addFilter } from "@wordpress/hooks"; import { useBlockEditContext, mayDisplayControlsKey, mayDisplayParentControlsKey } from "../components/block-edit/context"; import { useSettings } from "../components"; import { useSettingsForBlockElement } from "../components/global-styles/hooks"; import { getValueFromObjectPath, setImmutably } from "../utils/object"; import { store as blockEditorStore } from "../store"; import { unlock } from "../lock-unlock"; import clsx from "clsx"; import { jsx } from "react/jsx-runtime"; var cleanEmptyObject = (object) => { if (object === null || typeof object !== "object" || Array.isArray(object)) { return object; } const cleanedNestedObjects = Object.entries(object).map(([key, value]) => [key, cleanEmptyObject(value)]).filter(([, value]) => value !== void 0); return !cleanedNestedObjects.length ? void 0 : Object.fromEntries(cleanedNestedObjects); }; function transformStyles(activeSupports, migrationPaths, result, source, index, results) { if (Object.values(activeSupports ?? {}).every( (isActive) => !isActive )) { return result; } if (results.length === 1 && result.innerBlocks.length === source.length) { return result; } let referenceBlockAttributes = source[0]?.attributes; if (results.length > 1 && source.length > 1) { if (source[index]) { referenceBlockAttributes = source[index]?.attributes; } else { return result; } } let returnBlock = result; Object.entries(activeSupports).forEach(([support, isActive]) => { if (isActive) { migrationPaths[support].forEach((path) => { const styleValue = getValueFromObjectPath( referenceBlockAttributes, path ); if (styleValue) { returnBlock = { ...returnBlock, attributes: setImmutably( returnBlock.attributes, path, styleValue ) }; } }); } }); return returnBlock; } function shouldSkipSerialization(blockNameOrType, featureSet, feature) { const support = getBlockSupport(blockNameOrType, featureSet); const skipSerialization = support?.__experimentalSkipSerialization; if (Array.isArray(skipSerialization)) { return skipSerialization.includes(feature); } return skipSerialization; } var pendingStyleOverrides = /* @__PURE__ */ new WeakMap(); function useStyleOverride({ id, css }) { return usePrivateStyleOverride({ id, css }); } function usePrivateStyleOverride({ id, css, assets, __unstableType, variation, clientId } = {}) { const { setStyleOverride, deleteStyleOverride } = unlock( useDispatch(blockEditorStore) ); const registry = useRegistry(); const fallbackId = useId(); useEffect(() => { if (!css && !assets) { return; } const _id = id || fallbackId; const override = { id, css, assets, __unstableType, variation, clientId }; if (!pendingStyleOverrides.get(registry)) { pendingStyleOverrides.set(registry, []); } pendingStyleOverrides.get(registry).push([_id, override]); window.queueMicrotask(() => { if (pendingStyleOverrides.get(registry)?.length) { registry.batch(() => { pendingStyleOverrides.get(registry).forEach((args) => { setStyleOverride(...args); }); pendingStyleOverrides.set(registry, []); }); } }); return () => { const isPending = pendingStyleOverrides.get(registry)?.find(([currentId]) => currentId === _id); if (isPending) { pendingStyleOverrides.set( registry, pendingStyleOverrides.get(registry).filter(([currentId]) => currentId !== _id) ); } else { deleteStyleOverride(_id); } }; }, [ id, css, clientId, assets, __unstableType, fallbackId, setStyleOverride, deleteStyleOverride, registry ]); } function useBlockSettings(name, parentLayout) { const [ backgroundImage, backgroundSize, customFontFamilies, defaultFontFamilies, themeFontFamilies, defaultFontSizesEnabled, customFontSizes, defaultFontSizes, themeFontSizes, customFontSize, fontStyle, fontWeight, lineHeight, textAlign, textColumns, textDecoration, writingMode, textTransform, letterSpacing, padding, margin, blockGap, defaultSpacingSizesEnabled, customSpacingSize, userSpacingSizes, defaultSpacingSizes, themeSpacingSizes, units, aspectRatio, minHeight, width, layout, borderColor, borderRadius, borderStyle, borderWidth, borderRadiusSizes, customColorsEnabled, customColors, customDuotone, themeColors, defaultColors, defaultPalette, defaultDuotone, userDuotonePalette, themeDuotonePalette, defaultDuotonePalette, userGradientPalette, themeGradientPalette, defaultGradientPalette, defaultGradients, areCustomGradientsEnabled, isBackgroundEnabled, isLinkEnabled, isTextEnabled, isHeadingEnabled, isButtonEnabled, shadow ] = useSettings( "background.backgroundImage", "background.backgroundSize", "typography.fontFamilies.custom", "typography.fontFamilies.default", "typography.fontFamilies.theme", "typography.defaultFontSizes", "typography.fontSizes.custom", "typography.fontSizes.default", "typography.fontSizes.theme", "typography.customFontSize", "typography.fontStyle", "typography.fontWeight", "typography.lineHeight", "typography.textAlign", "typography.textColumns", "typography.textDecoration", "typography.writingMode", "typography.textTransform", "typography.letterSpacing", "spacing.padding", "spacing.margin", "spacing.blockGap", "spacing.defaultSpacingSizes", "spacing.customSpacingSize", "spacing.spacingSizes.custom", "spacing.spacingSizes.default", "spacing.spacingSizes.theme", "spacing.units", "dimensions.aspectRatio", "dimensions.minHeight", "dimensions.width", "layout", "border.color", "border.radius", "border.style", "border.width", "border.radiusSizes", "color.custom", "color.palette.custom", "color.customDuotone", "color.palette.theme", "color.palette.default", "color.defaultPalette", "color.defaultDuotone", "color.duotone.custom", "color.duotone.theme", "color.duotone.default", "color.gradients.custom", "color.gradients.theme", "color.gradients.default", "color.defaultGradients", "color.customGradient", "color.background", "color.link", "color.text", "color.heading", "color.button", "shadow" ); const rawSettings = useMemo(() => { return { background: { backgroundImage, backgroundSize }, color: { palette: { custom: customColors, theme: themeColors, default: defaultColors }, gradients: { custom: userGradientPalette, theme: themeGradientPalette, default: defaultGradientPalette }, duotone: { custom: userDuotonePalette, theme: themeDuotonePalette, default: defaultDuotonePalette }, defaultGradients, defaultPalette, defaultDuotone, custom: customColorsEnabled, customGradient: areCustomGradientsEnabled, customDuotone, background: isBackgroundEnabled, link: isLinkEnabled, heading: isHeadingEnabled, button: isButtonEnabled, text: isTextEnabled }, typography: { fontFamilies: { custom: customFontFamilies, default: defaultFontFamilies, theme: themeFontFamilies }, fontSizes: { custom: customFontSizes, default: defaultFontSizes, theme: themeFontSizes }, customFontSize, defaultFontSizes: defaultFontSizesEnabled, fontStyle, fontWeight, lineHeight, textAlign, textColumns, textDecoration, textTransform, letterSpacing, writingMode }, spacing: { spacingSizes: { custom: userSpacingSizes, default: defaultSpacingSizes, theme: themeSpacingSizes }, customSpacingSize, defaultSpacingSizes: defaultSpacingSizesEnabled, padding, margin, blockGap, units }, border: { color: borderColor, radius: borderRadius, style: borderStyle, width: borderWidth, radiusSizes: borderRadiusSizes }, dimensions: { aspectRatio, minHeight, width }, layout, parentLayout, shadow }; }, [ backgroundImage, backgroundSize, customFontFamilies, defaultFontFamilies, themeFontFamilies, defaultFontSizesEnabled, customFontSizes, defaultFontSizes, themeFontSizes, customFontSize, fontStyle, fontWeight, lineHeight, textAlign, textColumns, textDecoration, textTransform, letterSpacing, writingMode, padding, margin, blockGap, defaultSpacingSizesEnabled, customSpacingSize, userSpacingSizes, defaultSpacingSizes, themeSpacingSizes, units, aspectRatio, minHeight, width, layout, parentLayout, borderColor, borderRadius, borderStyle, borderWidth, borderRadiusSizes, customColorsEnabled, customColors, customDuotone, themeColors, defaultColors, defaultPalette, defaultDuotone, userDuotonePalette, themeDuotonePalette, defaultDuotonePalette, userGradientPalette, themeGradientPalette, defaultGradientPalette, defaultGradients, areCustomGradientsEnabled, isBackgroundEnabled, isLinkEnabled, isTextEnabled, isHeadingEnabled, isButtonEnabled, shadow ]); return useSettingsForBlockElement(rawSettings, name); } function createBlockEditFilter(features) { features = features.map((settings) => { return { ...settings, Edit: memo(settings.edit) }; }); const withBlockEditHooks = createHigherOrderComponent( (OriginalBlockEdit) => (props) => { const context = useBlockEditContext(); return [ ...features.map((feature, i) => { const { Edit, hasSupport, attributeKeys = [], shareWithChildBlocks } = feature; const shouldDisplayControls = context[mayDisplayControlsKey] || context[mayDisplayParentControlsKey] && shareWithChildBlocks; if (!shouldDisplayControls || !hasSupport(props.name)) { return null; } const neededProps = {}; for (const key of attributeKeys) { if (props.attributes[key]) { neededProps[key] = props.attributes[key]; } } return /* @__PURE__ */ jsx( Edit, { name: props.name, isSelected: props.isSelected, clientId: props.clientId, setAttributes: props.setAttributes, __unstableParentLayout: props.__unstableParentLayout, ...neededProps }, i ); }), /* @__PURE__ */ jsx(OriginalBlockEdit, { ...props }, "edit") ]; }, "withBlockEditHooks" ); addFilter("editor.BlockEdit", "core/editor/hooks", withBlockEditHooks); } function BlockProps({ index, useBlockProps: hook, setAllWrapperProps, ...props }) { const wrapperProps = hook(props); const setWrapperProps = (next) => setAllWrapperProps((prev) => { const nextAll = [...prev]; nextAll[index] = next; return nextAll; }); useEffect(() => { setWrapperProps(wrapperProps); return () => { setWrapperProps(void 0); }; }); return null; } var BlockPropsPure = memo(BlockProps); function createBlockListBlockFilter(features) { const withBlockListBlockHooks = createHigherOrderComponent( (BlockListBlock) => (props) => { const [allWrapperProps, setAllWrapperProps] = useState( Array(features.length).fill(void 0) ); return [ ...features.map((feature, i) => { const { hasSupport, attributeKeys = [], useBlockProps, isMatch } = feature; const neededProps = {}; for (const key of attributeKeys) { if (props.attributes[key]) { neededProps[key] = props.attributes[key]; } } if ( // Skip rendering if none of the needed attributes are // set. !Object.keys(neededProps).length || !hasSupport(props.name) || isMatch && !isMatch(neededProps) ) { return null; } return /* @__PURE__ */ jsx( BlockPropsPure, { index: i, useBlockProps, setAllWrapperProps, name: props.name, clientId: props.clientId, ...neededProps }, i ); }), /* @__PURE__ */ jsx( BlockListBlock, { ...props, wrapperProps: allWrapperProps.filter(Boolean).reduce((acc, wrapperProps) => { return { ...acc, ...wrapperProps, className: clsx( acc.className, wrapperProps.className ), style: { ...acc.style, ...wrapperProps.style } }; }, props.wrapperProps || {}) }, "edit" ) ]; }, "withBlockListBlockHooks" ); addFilter( "editor.BlockListBlock", "core/editor/hooks", withBlockListBlockHooks ); } function createBlockSaveFilter(features) { function extraPropsFromHooks(props, name, attributes) { return features.reduce((accu, feature) => { const { hasSupport, attributeKeys = [], addSaveProps } = feature; const neededAttributes = {}; for (const key of attributeKeys) { if (attributes[key]) { neededAttributes[key] = attributes[key]; } } if ( // Skip rendering if none of the needed attributes are // set. !Object.keys(neededAttributes).length || !hasSupport(name) ) { return accu; } return addSaveProps(accu, name, neededAttributes); }, props); } addFilter( "blocks.getSaveContent.extraProps", "core/editor/hooks", extraPropsFromHooks, 0 ); addFilter( "blocks.getSaveContent.extraProps", "core/editor/hooks", (props) => { if (props.hasOwnProperty("className") && !props.className) { delete props.className; } return props; } ); } export { cleanEmptyObject, createBlockEditFilter, createBlockListBlockFilter, createBlockSaveFilter, shouldSkipSerialization, transformStyles, useBlockSettings, usePrivateStyleOverride, useStyleOverride }; //# sourceMappingURL=utils.js.map