UNPKG

@wordpress/block-editor

Version:
370 lines (314 loc) 14.8 kB
import _extends from "@babel/runtime/helpers/esm/extends"; import { createElement } from "@wordpress/element"; /** * External dependencies */ import classnames from 'classnames'; import { isObject } from 'lodash'; /** * WordPress dependencies */ import { addFilter } from '@wordpress/hooks'; import { getBlockSupport } from '@wordpress/blocks'; import { __ } from '@wordpress/i18n'; import { useRef, useEffect, Platform } from '@wordpress/element'; import { createHigherOrderComponent } from '@wordpress/compose'; /** * Internal dependencies */ import { getColorClassName, getColorObjectByColorValue, getColorObjectByAttributeValues } from '../components/colors'; import { __experimentalGetGradientClass, getGradientValueBySlug, getGradientSlugByValue } from '../components/gradients'; import { cleanEmptyObject } from './utils'; import ColorPanel from './color-panel'; import useEditorFeature from '../components/use-editor-feature'; export const COLOR_SUPPORT_KEY = 'color'; const EMPTY_ARRAY = []; const hasColorSupport = blockType => { const colorSupport = getBlockSupport(blockType, COLOR_SUPPORT_KEY); return colorSupport && (colorSupport.link === true || colorSupport.gradient === true || colorSupport.background !== false || colorSupport.text !== false); }; const shouldSkipSerialization = blockType => { const colorSupport = getBlockSupport(blockType, COLOR_SUPPORT_KEY); return colorSupport === null || colorSupport === void 0 ? void 0 : colorSupport.__experimentalSkipSerialization; }; const hasLinkColorSupport = blockType => { if (Platform.OS !== 'web') { return false; } const colorSupport = getBlockSupport(blockType, COLOR_SUPPORT_KEY); return isObject(colorSupport) && !!colorSupport.link; }; const hasGradientSupport = blockType => { if (Platform.OS !== 'web') { return false; } const colorSupport = getBlockSupport(blockType, COLOR_SUPPORT_KEY); return isObject(colorSupport) && !!colorSupport.gradients; }; const hasBackgroundColorSupport = blockType => { const colorSupport = getBlockSupport(blockType, COLOR_SUPPORT_KEY); return colorSupport && colorSupport.background !== false; }; const hasTextColorSupport = blockType => { const colorSupport = getBlockSupport(blockType, COLOR_SUPPORT_KEY); return colorSupport && colorSupport.text !== false; }; /** * Filters registered block settings, extending attributes to include * `backgroundColor` and `textColor` attribute. * * @param {Object} settings Original block settings * @return {Object} Filtered block settings */ function addAttributes(settings) { if (!hasColorSupport(settings)) { return settings; } // allow blocks to specify their own attribute definition with default values if needed. if (!settings.attributes.backgroundColor) { Object.assign(settings.attributes, { backgroundColor: { type: 'string' } }); } if (!settings.attributes.textColor) { Object.assign(settings.attributes, { textColor: { type: 'string' } }); } if (hasGradientSupport(settings) && !settings.attributes.gradient) { Object.assign(settings.attributes, { gradient: { type: 'string' } }); } return settings; } /** * Override props assigned to save component to inject colors classnames. * * @param {Object} props Additional props applied to save element * @param {Object} blockType Block type * @param {Object} attributes Block attributes * @return {Object} Filtered props applied to save element */ export function addSaveProps(props, blockType, attributes) { var _style$color, _style$color2, _style$color3, _style$color4, _style$color5; if (!hasColorSupport(blockType) || shouldSkipSerialization(blockType)) { return props; } const hasGradient = hasGradientSupport(blockType); // I'd have prefered to avoid the "style" attribute usage here const { backgroundColor, textColor, gradient, style } = attributes; const backgroundClass = getColorClassName('background-color', backgroundColor); const gradientClass = __experimentalGetGradientClass(gradient); const textClass = getColorClassName('color', textColor); const newClassName = classnames(props.className, textClass, gradientClass, { // Don't apply the background class if there's a custom gradient [backgroundClass]: (!hasGradient || !(style !== null && style !== void 0 && (_style$color = style.color) !== null && _style$color !== void 0 && _style$color.gradient)) && !!backgroundClass, 'has-text-color': textColor || (style === null || style === void 0 ? void 0 : (_style$color2 = style.color) === null || _style$color2 === void 0 ? void 0 : _style$color2.text), 'has-background': backgroundColor || (style === null || style === void 0 ? void 0 : (_style$color3 = style.color) === null || _style$color3 === void 0 ? void 0 : _style$color3.background) || hasGradient && (gradient || (style === null || style === void 0 ? void 0 : (_style$color4 = style.color) === null || _style$color4 === void 0 ? void 0 : _style$color4.gradient)), 'has-link-color': style === null || style === void 0 ? void 0 : (_style$color5 = style.color) === null || _style$color5 === void 0 ? void 0 : _style$color5.link }); props.className = newClassName ? newClassName : undefined; return props; } /** * Filters registered block settings to extand the block edit wrapper * to apply the desired styles and classnames properly. * * @param {Object} settings Original block settings * @return {Object} Filtered block settings */ export function addEditProps(settings) { if (!hasColorSupport(settings) || shouldSkipSerialization(settings)) { return settings; } const existingGetEditWrapperProps = settings.getEditWrapperProps; settings.getEditWrapperProps = attributes => { let props = {}; if (existingGetEditWrapperProps) { props = existingGetEditWrapperProps(attributes); } return addSaveProps(props, settings, attributes); }; return settings; } const getLinkColorFromAttributeValue = (colors, value) => { const attributeParsed = /var:preset\|color\|(.+)/.exec(value); if (attributeParsed && attributeParsed[1]) { return getColorObjectByAttributeValues(colors, attributeParsed[1]).color; } return value; }; /** * Inspector control panel containing the color related configuration * * @param {Object} props * * @return {WPElement} Color edit element. */ export function ColorEdit(props) { var _style$color7, _style$color8, _style$color9, _style$color10, _props$attributes$sty2, _props$attributes$sty3; const { name: blockName, attributes } = props; const isLinkColorEnabled = useEditorFeature('color.link'); const colors = useEditorFeature('color.palette') || EMPTY_ARRAY; const gradients = useEditorFeature('color.gradients') || EMPTY_ARRAY; // Shouldn't be needed but right now the ColorGradientsPanel // can trigger both onChangeColor and onChangeBackground // synchronously causing our two callbacks to override changes // from each other. const localAttributes = useRef(attributes); useEffect(() => { localAttributes.current = attributes; }, [attributes]); if (!hasColorSupport(blockName) || Platform.OS !== 'web') { return null; } const hasBackground = hasBackgroundColorSupport(blockName); const hasGradient = hasGradientSupport(blockName); const { style, textColor, backgroundColor, gradient } = attributes; let gradientValue; if (hasGradient && gradient) { gradientValue = getGradientValueBySlug(gradients, gradient); } else if (hasGradient) { var _style$color6; gradientValue = style === null || style === void 0 ? void 0 : (_style$color6 = style.color) === null || _style$color6 === void 0 ? void 0 : _style$color6.gradient; } const onChangeColor = name => value => { var _localAttributes$curr, _localAttributes$curr2; const colorObject = getColorObjectByColorValue(colors, value); const attributeName = name + 'Color'; const newStyle = { ...localAttributes.current.style, color: { ...((_localAttributes$curr = localAttributes.current) === null || _localAttributes$curr === void 0 ? void 0 : (_localAttributes$curr2 = _localAttributes$curr.style) === null || _localAttributes$curr2 === void 0 ? void 0 : _localAttributes$curr2.color), [name]: colorObject !== null && colorObject !== void 0 && colorObject.slug ? undefined : value } }; const newNamedColor = colorObject !== null && colorObject !== void 0 && colorObject.slug ? colorObject.slug : undefined; const newAttributes = { style: cleanEmptyObject(newStyle), [attributeName]: newNamedColor }; props.setAttributes(newAttributes); localAttributes.current = { ...localAttributes.current, ...newAttributes }; }; const onChangeGradient = value => { const slug = getGradientSlugByValue(gradients, value); let newAttributes; if (slug) { var _localAttributes$curr3, _localAttributes$curr4, _localAttributes$curr5; const newStyle = { ...((_localAttributes$curr3 = localAttributes.current) === null || _localAttributes$curr3 === void 0 ? void 0 : _localAttributes$curr3.style), color: { ...((_localAttributes$curr4 = localAttributes.current) === null || _localAttributes$curr4 === void 0 ? void 0 : (_localAttributes$curr5 = _localAttributes$curr4.style) === null || _localAttributes$curr5 === void 0 ? void 0 : _localAttributes$curr5.color), gradient: undefined } }; newAttributes = { style: cleanEmptyObject(newStyle), gradient: slug }; } else { var _localAttributes$curr6, _localAttributes$curr7, _localAttributes$curr8; const newStyle = { ...((_localAttributes$curr6 = localAttributes.current) === null || _localAttributes$curr6 === void 0 ? void 0 : _localAttributes$curr6.style), color: { ...((_localAttributes$curr7 = localAttributes.current) === null || _localAttributes$curr7 === void 0 ? void 0 : (_localAttributes$curr8 = _localAttributes$curr7.style) === null || _localAttributes$curr8 === void 0 ? void 0 : _localAttributes$curr8.color), gradient: value } }; newAttributes = { style: cleanEmptyObject(newStyle), gradient: undefined }; } props.setAttributes(newAttributes); localAttributes.current = { ...localAttributes.current, ...newAttributes }; }; const onChangeLinkColor = value => { var _props$attributes$sty; const colorObject = getColorObjectByColorValue(colors, value); props.setAttributes({ style: { ...props.attributes.style, color: { ...((_props$attributes$sty = props.attributes.style) === null || _props$attributes$sty === void 0 ? void 0 : _props$attributes$sty.color), link: colorObject !== null && colorObject !== void 0 && colorObject.slug ? `var:preset|color|${colorObject.slug}` : value } } }); }; return createElement(ColorPanel, { enableContrastChecking: // Turn on contrast checker for web only since it's not supported on mobile yet. Platform.OS === 'web' && !gradient && !(style !== null && style !== void 0 && (_style$color7 = style.color) !== null && _style$color7 !== void 0 && _style$color7.gradient), clientId: props.clientId, settings: [...(hasTextColorSupport(blockName) ? [{ label: __('Text color'), onColorChange: onChangeColor('text'), colorValue: getColorObjectByAttributeValues(colors, textColor, style === null || style === void 0 ? void 0 : (_style$color8 = style.color) === null || _style$color8 === void 0 ? void 0 : _style$color8.text).color }] : []), ...(hasBackground || hasGradient ? [{ label: __('Background color'), onColorChange: hasBackground ? onChangeColor('background') : undefined, colorValue: getColorObjectByAttributeValues(colors, backgroundColor, style === null || style === void 0 ? void 0 : (_style$color9 = style.color) === null || _style$color9 === void 0 ? void 0 : _style$color9.background).color, gradientValue, onGradientChange: hasGradient ? onChangeGradient : undefined }] : []), ...(isLinkColorEnabled && hasLinkColorSupport(blockName) ? [{ label: __('Link Color'), onColorChange: onChangeLinkColor, colorValue: getLinkColorFromAttributeValue(colors, style === null || style === void 0 ? void 0 : (_style$color10 = style.color) === null || _style$color10 === void 0 ? void 0 : _style$color10.link), clearable: !!((_props$attributes$sty2 = props.attributes.style) !== null && _props$attributes$sty2 !== void 0 && (_props$attributes$sty3 = _props$attributes$sty2.color) !== null && _props$attributes$sty3 !== void 0 && _props$attributes$sty3.link) }] : [])] }); } /** * This adds inline styles for color palette colors. * Ideally, this is not needed and themes should load their palettes on the editor. * * @param {Function} BlockListBlock Original component * @return {Function} Wrapped component */ export const withColorPaletteStyles = createHigherOrderComponent(BlockListBlock => props => { var _getColorObjectByAttr, _getColorObjectByAttr2, _props$wrapperProps; const { name, attributes } = props; const { backgroundColor, textColor } = attributes; const colors = useEditorFeature('color.palette') || EMPTY_ARRAY; if (!hasColorSupport(name) || shouldSkipSerialization(name)) { return createElement(BlockListBlock, props); } const extraStyles = { color: textColor ? (_getColorObjectByAttr = getColorObjectByAttributeValues(colors, textColor)) === null || _getColorObjectByAttr === void 0 ? void 0 : _getColorObjectByAttr.color : undefined, backgroundColor: backgroundColor ? (_getColorObjectByAttr2 = getColorObjectByAttributeValues(colors, backgroundColor)) === null || _getColorObjectByAttr2 === void 0 ? void 0 : _getColorObjectByAttr2.color : undefined }; let wrapperProps = props.wrapperProps; wrapperProps = { ...props.wrapperProps, style: { ...extraStyles, ...((_props$wrapperProps = props.wrapperProps) === null || _props$wrapperProps === void 0 ? void 0 : _props$wrapperProps.style) } }; return createElement(BlockListBlock, _extends({}, props, { wrapperProps: wrapperProps })); }); addFilter('blocks.registerBlockType', 'core/color/addAttribute', addAttributes); addFilter('blocks.getSaveContent.extraProps', 'core/color/addSaveProps', addSaveProps); addFilter('blocks.registerBlockType', 'core/color/addEditProps', addEditProps); addFilter('editor.BlockListBlock', 'core/color/with-color-palette-styles', withColorPaletteStyles); //# sourceMappingURL=color.js.map