@wordpress/block-editor
Version:
303 lines (296 loc) • 11.5 kB
JavaScript
/**
* External dependencies
*/
import clsx from 'clsx';
/**
* WordPress dependencies
*/
import { addFilter } from '@wordpress/hooks';
import { getBlockSupport } from '@wordpress/blocks';
import { useMemo, Platform, useCallback } from '@wordpress/element';
import { useSelect } from '@wordpress/data';
/**
* Internal dependencies
*/
import { getColorClassName, getColorObjectByAttributeValues } from '../components/colors';
import { __experimentalGetGradientClass } from '../components/gradients';
import { cleanEmptyObject, transformStyles, shouldSkipSerialization } from './utils';
import { getBackgroundImageClasses } from './background';
import { useSettings } from '../components/use-settings';
import InspectorControls from '../components/inspector-controls';
import { useHasColorPanel, default as StylesColorPanel } from '../components/global-styles/color-panel';
import BlockColorContrastChecker from './contrast-checker';
import { store as blockEditorStore } from '../store';
import { jsx as _jsx } from "react/jsx-runtime";
export const COLOR_SUPPORT_KEY = 'color';
const hasColorSupport = blockNameOrType => {
const colorSupport = getBlockSupport(blockNameOrType, COLOR_SUPPORT_KEY);
return colorSupport && (colorSupport.link === true || colorSupport.gradient === true || colorSupport.background !== false || colorSupport.text !== false);
};
const hasLinkColorSupport = blockType => {
if (Platform.OS !== 'web') {
return false;
}
const colorSupport = getBlockSupport(blockType, COLOR_SUPPORT_KEY);
return colorSupport !== null && typeof colorSupport === 'object' && !!colorSupport.link;
};
const hasGradientSupport = blockNameOrType => {
const colorSupport = getBlockSupport(blockNameOrType, COLOR_SUPPORT_KEY);
return colorSupport !== null && typeof colorSupport === 'object' && !!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|string} blockNameOrType Block type.
* @param {Object} attributes Block attributes.
*
* @return {Object} Filtered props applied to save element.
*/
export function addSaveProps(props, blockNameOrType, attributes) {
if (!hasColorSupport(blockNameOrType) || shouldSkipSerialization(blockNameOrType, COLOR_SUPPORT_KEY)) {
return props;
}
const hasGradient = hasGradientSupport(blockNameOrType);
// I'd have preferred to avoid the "style" attribute usage here
const {
backgroundColor,
textColor,
gradient,
style
} = attributes;
const shouldSerialize = feature => !shouldSkipSerialization(blockNameOrType, COLOR_SUPPORT_KEY, feature);
// Primary color classes must come before the `has-text-color`,
// `has-background` and `has-link-color` classes to maintain backwards
// compatibility and avoid block invalidations.
const textClass = shouldSerialize('text') ? getColorClassName('color', textColor) : undefined;
const gradientClass = shouldSerialize('gradients') ? __experimentalGetGradientClass(gradient) : undefined;
const backgroundClass = shouldSerialize('background') ? getColorClassName('background-color', backgroundColor) : undefined;
const serializeHasBackground = shouldSerialize('background') || shouldSerialize('gradients');
const hasBackground = backgroundColor || style?.color?.background || hasGradient && (gradient || style?.color?.gradient);
const newClassName = clsx(props.className, textClass, gradientClass, {
// Don't apply the background class if there's a custom gradient.
[backgroundClass]: (!hasGradient || !style?.color?.gradient) && !!backgroundClass,
'has-text-color': shouldSerialize('text') && (textColor || style?.color?.text),
'has-background': serializeHasBackground && hasBackground,
'has-link-color': shouldSerialize('link') && style?.elements?.link?.color
});
props.className = newClassName ? newClassName : undefined;
return props;
}
function styleToAttributes(style) {
const textColorValue = style?.color?.text;
const textColorSlug = textColorValue?.startsWith('var:preset|color|') ? textColorValue.substring('var:preset|color|'.length) : undefined;
const backgroundColorValue = style?.color?.background;
const backgroundColorSlug = backgroundColorValue?.startsWith('var:preset|color|') ? backgroundColorValue.substring('var:preset|color|'.length) : undefined;
const gradientValue = style?.color?.gradient;
const gradientSlug = gradientValue?.startsWith('var:preset|gradient|') ? gradientValue.substring('var:preset|gradient|'.length) : undefined;
const updatedStyle = {
...style
};
updatedStyle.color = {
...updatedStyle.color,
text: textColorSlug ? undefined : textColorValue,
background: backgroundColorSlug ? undefined : backgroundColorValue,
gradient: gradientSlug ? undefined : gradientValue
};
return {
style: cleanEmptyObject(updatedStyle),
textColor: textColorSlug,
backgroundColor: backgroundColorSlug,
gradient: gradientSlug
};
}
function attributesToStyle(attributes) {
return {
...attributes.style,
color: {
...attributes.style?.color,
text: attributes.textColor ? 'var:preset|color|' + attributes.textColor : attributes.style?.color?.text,
background: attributes.backgroundColor ? 'var:preset|color|' + attributes.backgroundColor : attributes.style?.color?.background,
gradient: attributes.gradient ? 'var:preset|gradient|' + attributes.gradient : attributes.style?.color?.gradient
}
};
}
function ColorInspectorControl({
children,
resetAllFilter
}) {
const attributesResetAllFilter = useCallback(attributes => {
const existingStyle = attributesToStyle(attributes);
const updatedStyle = resetAllFilter(existingStyle);
return {
...attributes,
...styleToAttributes(updatedStyle)
};
}, [resetAllFilter]);
return /*#__PURE__*/_jsx(InspectorControls, {
group: "color",
resetAllFilter: attributesResetAllFilter,
children: children
});
}
export function ColorEdit({
clientId,
name,
setAttributes,
settings
}) {
const isEnabled = useHasColorPanel(settings);
function selector(select) {
const {
style,
textColor,
backgroundColor,
gradient
} = select(blockEditorStore).getBlockAttributes(clientId) || {};
return {
style,
textColor,
backgroundColor,
gradient
};
}
const {
style,
textColor,
backgroundColor,
gradient
} = useSelect(selector, [clientId]);
const value = useMemo(() => {
return attributesToStyle({
style,
textColor,
backgroundColor,
gradient
});
}, [style, textColor, backgroundColor, gradient]);
const onChange = newStyle => {
setAttributes(styleToAttributes(newStyle));
};
if (!isEnabled) {
return null;
}
const defaultControls = getBlockSupport(name, [COLOR_SUPPORT_KEY, '__experimentalDefaultControls']);
const enableContrastChecking = Platform.OS === 'web' && !value?.color?.gradient && (settings?.color?.text || settings?.color?.link) &&
// Contrast checking is enabled by default.
// Deactivating it requires `enableContrastChecker` to have
// an explicit value of `false`.
false !== getBlockSupport(name, [COLOR_SUPPORT_KEY, 'enableContrastChecker']);
return /*#__PURE__*/_jsx(StylesColorPanel, {
as: ColorInspectorControl,
panelId: clientId,
settings: settings,
value: value,
onChange: onChange,
defaultControls: defaultControls,
enableContrastChecker: false !== getBlockSupport(name, [COLOR_SUPPORT_KEY, 'enableContrastChecker']),
children: enableContrastChecking && /*#__PURE__*/_jsx(BlockColorContrastChecker, {
clientId: clientId
})
});
}
function useBlockProps({
name,
backgroundColor,
textColor,
gradient,
style
}) {
const [userPalette, themePalette, defaultPalette] = useSettings('color.palette.custom', 'color.palette.theme', 'color.palette.default');
const colors = useMemo(() => [...(userPalette || []), ...(themePalette || []), ...(defaultPalette || [])], [userPalette, themePalette, defaultPalette]);
if (!hasColorSupport(name) || shouldSkipSerialization(name, COLOR_SUPPORT_KEY)) {
return {};
}
const extraStyles = {};
if (textColor && !shouldSkipSerialization(name, COLOR_SUPPORT_KEY, 'text')) {
extraStyles.color = getColorObjectByAttributeValues(colors, textColor)?.color;
}
if (backgroundColor && !shouldSkipSerialization(name, COLOR_SUPPORT_KEY, 'background')) {
extraStyles.backgroundColor = getColorObjectByAttributeValues(colors, backgroundColor)?.color;
}
const saveProps = addSaveProps({
style: extraStyles
}, name, {
textColor,
backgroundColor,
gradient,
style
});
const hasBackgroundValue = backgroundColor || style?.color?.background || gradient || style?.color?.gradient;
return {
...saveProps,
className: clsx(saveProps.className,
// Add background image classes in the editor, if not already handled by background color values.
!hasBackgroundValue && getBackgroundImageClasses(style))
};
}
export default {
useBlockProps,
addSaveProps,
attributeKeys: ['backgroundColor', 'textColor', 'gradient', 'style'],
hasSupport: hasColorSupport
};
const MIGRATION_PATHS = {
linkColor: [['style', 'elements', 'link', 'color', 'text']],
textColor: [['textColor'], ['style', 'color', 'text']],
backgroundColor: [['backgroundColor'], ['style', 'color', 'background']],
gradient: [['gradient'], ['style', 'color', 'gradient']]
};
export function addTransforms(result, source, index, results) {
const destinationBlockType = result.name;
const activeSupports = {
linkColor: hasLinkColorSupport(destinationBlockType),
textColor: hasTextColorSupport(destinationBlockType),
backgroundColor: hasBackgroundColorSupport(destinationBlockType),
gradient: hasGradientSupport(destinationBlockType)
};
return transformStyles(activeSupports, MIGRATION_PATHS, result, source, index, results);
}
addFilter('blocks.registerBlockType', 'core/color/addAttribute', addAttributes);
addFilter('blocks.switchToBlockType.transformedBlock', 'core/color/addTransforms', addTransforms);
//# sourceMappingURL=color.js.map