Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add the possibility to support gradients using the experimental color support flag #21481

Merged
merged 2 commits into from
Apr 8, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions packages/block-editor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,32 @@ _Returns_

- `Object`: Font size object.

<a name="getGradientSlugByValue" href="#getGradientSlugByValue">#</a> **getGradientSlugByValue**

Retrieves the gradient slug per slug.

_Parameters_

- _gradients_ `Array`: Gradient Palette
- _value_ `string`: Gradient value

_Returns_

- `string`: Gradient slug.

<a name="getGradientValueBySlug" href="#getGradientValueBySlug">#</a> **getGradientValueBySlug**

Retrieves the gradient value per slug.

_Parameters_

- _gradients_ `Array`: Gradient Palette
- _slug_ `string`: Gradient slug

_Returns_

- `string`: Gradient value.

<a name="InnerBlocks" href="#InnerBlocks">#</a> **InnerBlocks**

_Related_
Expand Down
19 changes: 17 additions & 2 deletions packages/block-editor/src/components/gradients/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,15 @@ export function __experimentalGetGradientClass( gradientSlug ) {
return `has-${ gradientSlug }-gradient-background`;
}

function getGradientValueBySlug( gradients, slug ) {
/**
* Retrieves the gradient value per slug.
*
* @param {Array} gradients Gradient Palette
* @param {string} slug Gradient slug
*
* @return {string} Gradient value.
*/
export function getGradientValueBySlug( gradients, slug ) {
const gradient = find( gradients, [ 'slug', slug ] );
return gradient && gradient.gradient;
}
Expand All @@ -34,7 +42,14 @@ export function __experimentalGetGradientObjectByGradientValue(
return gradient;
}

function getGradientSlugByValue( gradients, value ) {
/**
* Retrieves the gradient slug per slug.
*
* @param {Array} gradients Gradient Palette
* @param {string} value Gradient value
* @return {string} Gradient slug.
*/
export function getGradientSlugByValue( gradients, value ) {
const gradient = __experimentalGetGradientObjectByGradientValue(
gradients,
value
Expand Down
31 changes: 22 additions & 9 deletions packages/block-editor/src/hooks/color-panel.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,30 @@ import { useState, useEffect } from '@wordpress/element';
/**
* Internal dependencies
*/
import PanelColorSettings from '../components/panel-color-settings';
import PanelColorGradientSettings from '../components/colors-gradients/panel-color-gradient-settings';
import ContrastChecker from '../components/contrast-checker';
import InspectorControls from '../components/inspector-controls';
import { getBlockDOMNode } from '../utils/dom';

export default function ColorPanel( { colorSettings, clientId } ) {
export default function ColorPanel( {
settings,
clientId,
enableContrastChecking = true,
} ) {
const { getComputedStyle, Node } = window;

const [ detectedBackgroundColor, setDetectedBackgroundColor ] = useState();
const [ detectedColor, setDetectedColor ] = useState();

useEffect( () => {
if ( ! enableContrastChecking ) {
return;
}

const colorsDetectionElement = getBlockDOMNode( clientId );
if ( ! colorsDetectionElement ) {
return;
}
setDetectedColor( getComputedStyle( colorsDetectionElement ).color );

let backgroundColorNode = colorsDetectionElement;
Expand All @@ -40,16 +51,18 @@ export default function ColorPanel( { colorSettings, clientId } ) {

return (
<InspectorControls>
<PanelColorSettings
<PanelColorGradientSettings
title={ __( 'Color settings' ) }
initialOpen={ false }
colorSettings={ colorSettings }
settings={ settings }
>
<ContrastChecker
backgroundColor={ detectedBackgroundColor }
textColor={ detectedColor }
/>
</PanelColorSettings>
{ enableContrastChecking && (
<ContrastChecker
backgroundColor={ detectedBackgroundColor }
textColor={ detectedColor }
/>
) }
</PanelColorGradientSettings>
</InspectorControls>
);
}
143 changes: 119 additions & 24 deletions packages/block-editor/src/hooks/color.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@
* External dependencies
*/
import classnames from 'classnames';
import { isObject } from 'lodash';

/**
* WordPress dependencies
*/
import { addFilter } from '@wordpress/hooks';
import { hasBlockSupport } from '@wordpress/blocks';
import { hasBlockSupport, getBlockSupport } from '@wordpress/blocks';
import { __ } from '@wordpress/i18n';
import { useSelect } from '@wordpress/data';
import { useRef, useEffect } from '@wordpress/element';

/**
* Internal dependencies
Expand All @@ -19,11 +21,25 @@ import {
getColorObjectByColorValue,
getColorObjectByAttributeValues,
} from '../components/colors';
import {
__experimentalGetGradientClass,
getGradientValueBySlug,
getGradientSlugByValue,
} from '../components/gradients';
import { cleanEmptyObject } from './utils';
import ColorPanel from './color-panel';

export const COLOR_SUPPORT_KEY = '__experimentalColor';

const hasColorSupport = ( blockType ) =>
hasBlockSupport( blockType, COLOR_SUPPORT_KEY );

const hasGradientSupport = ( blockType ) => {
const colorSupport = getBlockSupport( blockType, COLOR_SUPPORT_KEY );

return isObject( colorSupport ) && !! colorSupport.gradients;
};

/**
* Filters registered block settings, extending attributes to include
* `backgroundColor` and `textColor` attribute.
Expand All @@ -32,7 +48,7 @@ export const COLOR_SUPPORT_KEY = '__experimentalColor';
* @return {Object} Filtered block settings
*/
function addAttributes( settings ) {
if ( ! hasBlockSupport( settings, COLOR_SUPPORT_KEY ) ) {
if ( ! hasColorSupport( settings ) ) {
return settings;
}

Expand All @@ -52,6 +68,14 @@ function addAttributes( settings ) {
} );
}

if ( hasGradientSupport( settings ) && ! settings.attributes.gradient ) {
Object.assign( settings.attributes, {
gradient: {
type: 'string',
},
} );
}

return settings;
}

Expand All @@ -64,25 +88,35 @@ function addAttributes( settings ) {
* @return {Object} Filtered props applied to save element
*/
export function addSaveProps( props, blockType, attributes ) {
if ( ! hasBlockSupport( blockType, COLOR_SUPPORT_KEY ) ) {
if ( ! hasColorSupport( blockType ) ) {
return props;
}

const hasGradient = hasGradientSupport( blockType );

// I'd have prefered to avoid the "style" attribute usage here
const { backgroundColor, textColor, style } = attributes;
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,
backgroundClass,
textClass,
gradientClass,
{
// Don't apply the background class if there's a custom gradient
[ backgroundClass ]:
( ! hasGradient || ! style?.color?.gradient ) &&
!! backgroundClass,
'has-text-color': textColor || style?.color?.text,
'has-background': backgroundColor || style?.color?.background,
'has-background':
backgroundColor ||
style?.color?.background ||
( hasGradient && ( gradient || style?.color?.gradient ) ),
}
);
props.className = newClassName ? newClassName : undefined;
Expand All @@ -98,7 +132,7 @@ export function addSaveProps( props, blockType, attributes ) {
* @return {Object} Filtered block settings
*/
export function addEditProps( settings ) {
if ( ! hasBlockSupport( settings, COLOR_SUPPORT_KEY ) ) {
if ( ! hasColorSupport( settings ) ) {
return settings;
}
const existingGetEditWrapperProps = settings.getEditWrapperProps;
Expand All @@ -121,57 +155,118 @@ export function addEditProps( settings ) {
* @return {WPElement} Color edit element.
*/
export function ColorEdit( props ) {
const { name: blockName } = props;
const colors = useSelect( ( select ) => {
return select( 'core/block-editor' ).getSettings().colors;
const { name: blockName, attributes } = props;
const { colors, gradients } = useSelect( ( select ) => {
return select( 'core/block-editor' ).getSettings();
}, [] );
// 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 ( ! hasBlockSupport( blockName, COLOR_SUPPORT_KEY ) ) {
if ( ! hasColorSupport( blockName ) ) {
return null;
}
const { style, textColor, backgroundColor } = props.attributes;

const hasGradient = hasGradientSupport( blockName );

const { style, textColor, backgroundColor, gradient } = attributes;
let gradientValue;
if ( hasGradient && gradient ) {
gradientValue = getGradientValueBySlug( gradients, gradient );
} else if ( hasGradient ) {
gradientValue = style?.color?.gradient;
}

const onChangeColor = ( name ) => ( value ) => {
const colorObject = getColorObjectByColorValue( colors, value );
const attributeName = name + 'Color';
const newStyle = {
...style,
...localAttributes.current.style,
color: {
...style?.color,
...localAttributes.current?.style?.color,
[ name ]: colorObject?.slug ? undefined : value,
},
};

const newNamedColor = colorObject?.slug ? colorObject.slug : undefined;
props.setAttributes( {
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 ) {
const newStyle = {
...localAttributes.current?.style,
color: {
...localAttributes.current?.style?.color,
gradient: undefined,
},
};
newAttributes = {
style: cleanEmptyObject( newStyle ),
gradient: slug,
};
} else {
const newStyle = {
...localAttributes.current?.style,
color: {
...localAttributes.current?.style?.color,
gradient: value,
},
};
newAttributes = {
style: cleanEmptyObject( newStyle ),
gradient: undefined,
};
}
props.setAttributes( newAttributes );
localAttributes.current = {
...localAttributes.current,
...newAttributes,
};
};

return (
<ColorPanel
key="colors"
enableContrastChecking={ ! gradient && ! style?.color?.gradient }
clientId={ props.clientId }
colorSettings={ [
settings={ [
{
label: __( 'Text Color' ),
onChange: onChangeColor( 'text' ),
colors,
value: getColorObjectByAttributeValues(
onColorChange: onChangeColor( 'text' ),
colorValue: getColorObjectByAttributeValues(
colors,
textColor,
style?.color?.text
).color,
},
{
label: __( 'Background Color' ),
onChange: onChangeColor( 'background' ),
colors,
value: getColorObjectByAttributeValues(
onColorChange: onChangeColor( 'background' ),
colorValue: getColorObjectByAttributeValues(
colors,
backgroundColor,
style?.color?.background
).color,
gradientValue,
onGradientChange: hasGradient
? onChangeGradient
: undefined,
},
] }
/>
Expand Down
1 change: 1 addition & 0 deletions packages/block-editor/src/hooks/style.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export function getInlineStyles( styles = {} ) {
const mappings = {
lineHeight: [ 'typography', 'lineHeight' ],
fontSize: [ 'typography', 'fontSize' ],
background: [ 'color', 'gradient' ],
backgroundColor: [ 'color', 'background' ],
color: [ 'color', 'text' ],
};
Expand Down