@gechiui/block-editor
Version:
404 lines (341 loc) • 17.3 kB
JavaScript
import _extends from "@babel/runtime/helpers/esm/extends";
import { createElement } from "@gechiui/element";
/**
* External dependencies
*/
import classnames from 'classnames';
import { isObject, setWith, clone } from 'lodash';
/**
* GeChiUI dependencies
*/
import { addFilter } from '@gechiui/hooks';
import { getBlockSupport } from '@gechiui/blocks';
import { __ } from '@gechiui/i18n';
import { useRef, useEffect, useMemo, Platform } from '@gechiui/element';
import { createHigherOrderComponent } from '@gechiui/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 useSetting from '../components/use-setting';
export const COLOR_SUPPORT_KEY = 'color';
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 => {
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$elements, _style$elements$link;
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$elements = style.elements) === null || _style$elements === void 0 ? void 0 : (_style$elements$link = _style$elements.link) === null || _style$elements$link === void 0 ? void 0 : _style$elements$link.color
});
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;
};
function immutableSet(object, path, value) {
return setWith(object ? clone(object) : {}, path, value, clone);
}
/**
* Inspector control panel containing the color related configuration
*
* @param {Object} props
*
* @return {GCElement} Color edit element.
*/
export function ColorEdit(props) {
var _style$color6, _style$color7, _style$color8, _style$elements2, _style$elements2$link, _style$elements2$link2, _style$elements3, _style$elements3$link, _style$elements3$link2;
const {
name: blockName,
attributes
} = props; // Some color settings have a special handling for deprecated flags in `useSetting`,
// so we can't unwrap them by doing const { ... } = useSetting('color')
// until https://github.com/GeChiUI/gutenberg/issues/37094 is fixed.
const userPalette = useSetting('color.palette.custom');
const themePalette = useSetting('color.palette.theme');
const defaultPalette = useSetting('color.palette.default');
const allSolids = useMemo(() => [...(userPalette || []), ...(themePalette || []), ...(defaultPalette || [])], [userPalette, themePalette, defaultPalette]);
const userGradientPalette = useSetting('color.gradients.custom');
const themeGradientPalette = useSetting('color.gradients.theme');
const defaultGradientPalette = useSetting('color.gradients.default');
const allGradients = useMemo(() => [...(userGradientPalette || []), ...(themeGradientPalette || []), ...(defaultGradientPalette || [])], [userGradientPalette, themeGradientPalette, defaultGradientPalette]);
const areCustomSolidsEnabled = useSetting('color.custom');
const areCustomGradientsEnabled = useSetting('color.customGradient');
const isBackgroundEnabled = useSetting('color.background');
const isLinkEnabled = useSetting('color.link');
const isTextEnabled = useSetting('color.text');
const solidsEnabled = areCustomSolidsEnabled || !themePalette || (themePalette === null || themePalette === void 0 ? void 0 : themePalette.length) > 0;
const gradientsEnabled = areCustomGradientsEnabled || !themeGradientPalette || (themeGradientPalette === null || themeGradientPalette === void 0 ? void 0 : themeGradientPalette.length) > 0; // 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)) {
return null;
}
const hasLinkColor = hasLinkColorSupport(blockName) && isLinkEnabled && solidsEnabled;
const hasTextColor = hasTextColorSupport(blockName) && isTextEnabled && solidsEnabled;
const hasBackgroundColor = hasBackgroundColorSupport(blockName) && isBackgroundEnabled && solidsEnabled;
const hasGradientColor = hasGradientSupport(blockName) && gradientsEnabled;
if (!hasLinkColor && !hasTextColor && !hasBackgroundColor && !hasGradientColor) {
return null;
}
const {
style,
textColor,
backgroundColor,
gradient
} = attributes;
let gradientValue;
if (hasGradientColor && gradient) {
gradientValue = getGradientValueBySlug(allGradients, gradient);
} else if (hasGradientColor) {
var _style$color5;
gradientValue = style === null || style === void 0 ? void 0 : (_style$color5 = style.color) === null || _style$color5 === void 0 ? void 0 : _style$color5.gradient;
}
const onChangeColor = name => value => {
var _localAttributes$curr, _localAttributes$curr2;
const colorObject = getColorObjectByColorValue(allSolids, 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(allGradients, 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 => {
const colorObject = getColorObjectByColorValue(allSolids, value);
const newLinkColorValue = colorObject !== null && colorObject !== void 0 && colorObject.slug ? `var:preset|color|${colorObject.slug}` : value;
const newStyle = cleanEmptyObject(immutableSet(style, ['elements', 'link', 'color', 'text'], newLinkColorValue));
props.setAttributes({
style: newStyle
});
};
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$color6 = style.color) !== null && _style$color6 !== void 0 && _style$color6.gradient),
clientId: props.clientId,
settings: [...(hasTextColor ? [{
label: __('文本'),
onColorChange: onChangeColor('text'),
colorValue: getColorObjectByAttributeValues(allSolids, textColor, style === null || style === void 0 ? void 0 : (_style$color7 = style.color) === null || _style$color7 === void 0 ? void 0 : _style$color7.text).color
}] : []), ...(hasBackgroundColor || hasGradientColor ? [{
label: __('背景'),
onColorChange: hasBackgroundColor ? onChangeColor('background') : undefined,
colorValue: getColorObjectByAttributeValues(allSolids, backgroundColor, style === null || style === void 0 ? void 0 : (_style$color8 = style.color) === null || _style$color8 === void 0 ? void 0 : _style$color8.background).color,
gradientValue,
onGradientChange: hasGradientColor ? onChangeGradient : undefined
}] : []), ...(hasLinkColor ? [{
label: __('链接'),
onColorChange: onChangeLinkColor,
colorValue: getLinkColorFromAttributeValue(allSolids, style === null || style === void 0 ? void 0 : (_style$elements2 = style.elements) === null || _style$elements2 === void 0 ? void 0 : (_style$elements2$link = _style$elements2.link) === null || _style$elements2$link === void 0 ? void 0 : (_style$elements2$link2 = _style$elements2$link.color) === null || _style$elements2$link2 === void 0 ? void 0 : _style$elements2$link2.text),
clearable: !!(style !== null && style !== void 0 && (_style$elements3 = style.elements) !== null && _style$elements3 !== void 0 && (_style$elements3$link = _style$elements3.link) !== null && _style$elements3$link !== void 0 && (_style$elements3$link2 = _style$elements3$link.color) !== null && _style$elements3$link2 !== void 0 && _style$elements3$link2.text)
}] : [])]
});
}
/**
* 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 _props$wrapperProps;
const {
name,
attributes
} = props;
const {
backgroundColor,
textColor
} = attributes;
const userPalette = useSetting('color.palette.custom') || [];
const themePalette = useSetting('color.palette.theme') || [];
const defaultPalette = useSetting('color.palette.default') || [];
const colors = useMemo(() => [...(userPalette || []), ...(themePalette || []), ...(defaultPalette || [])], [userPalette, themePalette, defaultPalette]);
if (!hasColorSupport(name) || shouldSkipSerialization(name)) {
return createElement(BlockListBlock, props);
}
const extraStyles = {};
if (textColor) {
var _getColorObjectByAttr;
extraStyles.color = (_getColorObjectByAttr = getColorObjectByAttributeValues(colors, textColor)) === null || _getColorObjectByAttr === void 0 ? void 0 : _getColorObjectByAttr.color;
}
if (backgroundColor) {
var _getColorObjectByAttr2;
extraStyles.backgroundColor = (_getColorObjectByAttr2 = getColorObjectByAttributeValues(colors, backgroundColor)) === null || _getColorObjectByAttr2 === void 0 ? void 0 : _getColorObjectByAttr2.color;
}
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