UNPKG

@gechiui/block-editor

Version:
404 lines (341 loc) 17.3 kB
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